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_INTERNAL_DB_CORRUPTION;
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(1,("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_INTERNAL_DB_CORRUPTION;
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_INTERNAL_DB_CORRUPTION;
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(1,("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(0,("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_INTERNAL_DB_CORRUPTION;
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_INTERNAL_DB_CORRUPTION;
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 status = NT_STATUS_UNSUCCESSFUL;
446 /* And this confirms it in a transaction commit */
447 ret = ldb_transaction_commit(sam_ctx);
449 DEBUG(0,("Failed to commit transaction to change password on %s: %s\n",
450 ldb_dn_linearize(mem_ctx, user_dn),
451 ldb_errstring(sam_ctx)));
452 status = NT_STATUS_INTERNAL_DB_CORRUPTION;
459 ldb_transaction_cancel(sam_ctx);
460 talloc_free(sam_ctx);
462 reject = talloc(mem_ctx, struct samr_ChangeReject);
463 r->out.dominfo = dominfo;
464 r->out.reject = reject;
466 if (reject == NULL) {
469 ZERO_STRUCTP(reject);
471 reject->reason = reason;
478 samr_ChangePasswordUser2
480 easy - just a subset of samr_ChangePasswordUser3
482 NTSTATUS samr_ChangePasswordUser2(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
483 struct samr_ChangePasswordUser2 *r)
485 struct samr_ChangePasswordUser3 r2;
487 r2.in.server = r->in.server;
488 r2.in.account = r->in.account;
489 r2.in.nt_password = r->in.nt_password;
490 r2.in.nt_verifier = r->in.nt_verifier;
491 r2.in.lm_change = r->in.lm_change;
492 r2.in.lm_password = r->in.lm_password;
493 r2.in.lm_verifier = r->in.lm_verifier;
494 r2.in.password3 = NULL;
496 return samr_ChangePasswordUser3(dce_call, mem_ctx, &r2);
501 check that a password is sufficiently complex
503 static BOOL samdb_password_complexity_ok(const char *pass)
505 return check_password_quality(pass);
509 set the user password using plaintext, obeying any user or domain
510 password restrictions
512 note that this function doesn't actually store the result in the
513 database, it just fills in the "mod" structure with ldb modify
514 elements to setup the correct change when samdb_replace() is
515 called. This allows the caller to combine the change with other
516 changes (as is needed by some of the set user info levels)
518 The caller should probably have a transaction wrapping this
520 NTSTATUS samdb_set_password(struct ldb_context *ctx, TALLOC_CTX *mem_ctx,
521 const struct ldb_dn *user_dn,
522 const struct ldb_dn *domain_dn,
523 struct ldb_message *mod,
524 const char *new_pass,
525 struct samr_Password *lmNewHash,
526 struct samr_Password *ntNewHash,
529 enum samr_RejectReason *reject_reason,
530 struct samr_DomInfo1 **_dominfo)
532 const char * const user_attrs[] = { "userAccountControl", "lmPwdHistory",
534 "lmPwdHash", "ntPwdHash",
536 "pwdLastSet", NULL };
537 const char * const domain_attrs[] = { "pwdProperties", "pwdHistoryLength",
538 "maxPwdAge", "minPwdAge",
539 "minPwdLength", NULL };
542 uint_t minPwdLength, pwdProperties, pwdHistoryLength;
543 uint_t userAccountControl;
544 struct samr_Password *lmPwdHistory, *ntPwdHistory, *lmPwdHash, *ntPwdHash;
545 struct samr_Password local_lmNewHash, local_ntNewHash;
546 int lmPwdHistory_len, ntPwdHistory_len;
547 struct dom_sid *domain_sid;
548 struct ldb_message **res;
550 time_t now = time(NULL);
554 /* we need to know the time to compute password age */
555 unix_to_nt_time(&now_nt, now);
557 /* pull all the user parameters */
558 count = gendb_search_dn(ctx, mem_ctx, user_dn, &res, user_attrs);
560 return NT_STATUS_INTERNAL_DB_CORRUPTION;
562 userAccountControl = samdb_result_uint(res[0], "userAccountControl", 0);
563 lmPwdHistory_len = samdb_result_hashes(mem_ctx, res[0],
564 "lmPwdHistory", &lmPwdHistory);
565 ntPwdHistory_len = samdb_result_hashes(mem_ctx, res[0],
566 "ntPwdHistory", &ntPwdHistory);
567 lmPwdHash = samdb_result_hash(mem_ctx, res[0], "lmPwdHash");
568 ntPwdHash = samdb_result_hash(mem_ctx, res[0], "ntPwdHash");
569 pwdLastSet = samdb_result_uint64(res[0], "pwdLastSet", 0);
572 /* pull the domain parameters */
573 count = gendb_search_dn(ctx, mem_ctx, domain_dn, &res, domain_attrs);
575 return NT_STATUS_NO_SUCH_DOMAIN;
578 /* work out the domain sid, and pull the domain from there */
579 domain_sid = samdb_result_sid_prefix(mem_ctx, res[0], "objectSid");
580 if (domain_sid == NULL) {
581 return NT_STATUS_INTERNAL_DB_CORRUPTION;
584 count = gendb_search(ctx, mem_ctx, NULL, &res, domain_attrs,
586 ldap_encode_ndr_dom_sid(mem_ctx, domain_sid));
588 return NT_STATUS_NO_SUCH_DOMAIN;
592 pwdProperties = samdb_result_uint(res[0], "pwdProperties", 0);
593 pwdHistoryLength = samdb_result_uint(res[0], "pwdHistoryLength", 0);
594 minPwdLength = samdb_result_uint(res[0], "minPwdLength", 0);
595 minPwdAge = samdb_result_int64(res[0], "minPwdAge", 0);
598 struct samr_DomInfo1 *dominfo;
599 /* on failure we need to fill in the reject reasons */
600 dominfo = talloc(mem_ctx, struct samr_DomInfo1);
601 if (dominfo == NULL) {
602 return NT_STATUS_NO_MEMORY;
604 dominfo->min_password_length = minPwdLength;
605 dominfo->password_properties = pwdProperties;
606 dominfo->password_history_length = pwdHistoryLength;
607 dominfo->max_password_age = minPwdAge;
608 dominfo->min_password_age = minPwdAge;
613 /* check the various password restrictions */
614 if (restrictions && minPwdLength > strlen_m(new_pass)) {
616 *reject_reason = SAMR_REJECT_TOO_SHORT;
618 return NT_STATUS_PASSWORD_RESTRICTION;
621 /* possibly check password complexity */
622 if (restrictions && pwdProperties & DOMAIN_PASSWORD_COMPLEX &&
623 !samdb_password_complexity_ok(new_pass)) {
625 *reject_reason = SAMR_REJECT_COMPLEXITY;
627 return NT_STATUS_PASSWORD_RESTRICTION;
630 /* compute the new nt and lm hashes */
631 if (E_deshash(new_pass, local_lmNewHash.hash)) {
632 lmNewHash = &local_lmNewHash;
634 E_md4hash(new_pass, local_ntNewHash.hash);
635 ntNewHash = &local_ntNewHash;
638 if (restrictions && user_change) {
639 /* are all password changes disallowed? */
640 if (pwdProperties & DOMAIN_REFUSE_PASSWORD_CHANGE) {
642 *reject_reason = SAMR_REJECT_OTHER;
644 return NT_STATUS_PASSWORD_RESTRICTION;
647 /* can this user change password? */
648 if (userAccountControl & UF_PASSWD_CANT_CHANGE) {
650 *reject_reason = SAMR_REJECT_OTHER;
652 return NT_STATUS_PASSWORD_RESTRICTION;
655 /* yes, this is a minus. The ages are in negative 100nsec units! */
656 if (pwdLastSet - minPwdAge > now_nt) {
658 *reject_reason = SAMR_REJECT_OTHER;
660 return NT_STATUS_PASSWORD_RESTRICTION;
663 /* check the immediately past password */
664 if (pwdHistoryLength > 0) {
665 if (lmNewHash && lmPwdHash && memcmp(lmNewHash->hash, lmPwdHash->hash, 16) == 0) {
667 *reject_reason = SAMR_REJECT_COMPLEXITY;
669 return NT_STATUS_PASSWORD_RESTRICTION;
671 if (ntNewHash && ntPwdHash && memcmp(ntNewHash->hash, ntPwdHash->hash, 16) == 0) {
673 *reject_reason = SAMR_REJECT_COMPLEXITY;
675 return NT_STATUS_PASSWORD_RESTRICTION;
679 /* check the password history */
680 lmPwdHistory_len = MIN(lmPwdHistory_len, pwdHistoryLength);
681 ntPwdHistory_len = MIN(ntPwdHistory_len, pwdHistoryLength);
683 for (i=0; lmNewHash && i<lmPwdHistory_len;i++) {
684 if (memcmp(lmNewHash->hash, lmPwdHistory[i].hash, 16) == 0) {
686 *reject_reason = SAMR_REJECT_COMPLEXITY;
688 return NT_STATUS_PASSWORD_RESTRICTION;
691 for (i=0; ntNewHash && i<ntPwdHistory_len;i++) {
692 if (memcmp(ntNewHash->hash, ntPwdHistory[i].hash, 16) == 0) {
694 *reject_reason = SAMR_REJECT_COMPLEXITY;
696 return NT_STATUS_PASSWORD_RESTRICTION;
701 #define CHECK_RET(x) do { if (x != 0) return NT_STATUS_NO_MEMORY; } while(0)
703 /* the password is acceptable. Start forming the new fields */
705 /* if we know the cleartext, then only set it.
706 * Modules in ldb will set all the appropriate
708 CHECK_RET(samdb_msg_add_string(ctx, mem_ctx, mod,
709 "unicodePwd", new_pass));
711 /* We don't have the cleartext, so delete the old one
712 * and set what we have of the hashes */
713 CHECK_RET(samdb_msg_add_delete(ctx, mem_ctx, mod, "unicodePwd"));
716 CHECK_RET(samdb_msg_add_hash(ctx, mem_ctx, mod, "lmPwdHash", lmNewHash));
718 CHECK_RET(samdb_msg_add_delete(ctx, mem_ctx, mod, "lmPwdHash"));
722 CHECK_RET(samdb_msg_add_hash(ctx, mem_ctx, mod, "ntPwdHash", ntNewHash));
724 CHECK_RET(samdb_msg_add_delete(ctx, mem_ctx, mod, "ntPwdHash"));
732 set password via a samr_CryptPassword buffer
733 this will in the 'msg' with modify operations that will update the user
734 password when applied
736 NTSTATUS samr_set_password(struct dcesrv_call_state *dce_call,
738 const struct ldb_dn *account_dn, const struct ldb_dn *domain_dn,
740 struct ldb_message *msg,
741 struct samr_CryptPassword *pwbuf)
745 uint32_t new_pass_len;
746 DATA_BLOB session_key = data_blob(NULL, 0);
748 nt_status = dcesrv_fetch_session_key(dce_call->conn, &session_key);
749 if (!NT_STATUS_IS_OK(nt_status)) {
753 arcfour_crypt_blob(pwbuf->data, 516, &session_key);
755 if (!decode_pw_buffer(pwbuf->data, new_pass, sizeof(new_pass),
756 &new_pass_len, STR_UNICODE)) {
757 DEBUG(3,("samr: failed to decode password buffer\n"));
758 return NT_STATUS_WRONG_PASSWORD;
761 /* set the password - samdb needs to know both the domain and user DNs,
762 so the domain password policy can be used */
763 return samdb_set_password(sam_ctx, mem_ctx,
764 account_dn, domain_dn,
767 False, /* This is a password set, not change */
768 True, /* run restriction tests */
774 set password via a samr_CryptPasswordEx buffer
775 this will in the 'msg' with modify operations that will update the user
776 password when applied
778 NTSTATUS samr_set_password_ex(struct dcesrv_call_state *dce_call,
779 struct ldb_context *sam_ctx,
780 const struct ldb_dn *account_dn, const struct ldb_dn *domain_dn,
782 struct ldb_message *msg,
783 struct samr_CryptPasswordEx *pwbuf)
787 uint32_t new_pass_len;
788 DATA_BLOB co_session_key;
789 DATA_BLOB session_key = data_blob(NULL, 0);
790 struct MD5Context ctx;
792 nt_status = dcesrv_fetch_session_key(dce_call->conn, &session_key);
793 if (!NT_STATUS_IS_OK(nt_status)) {
797 co_session_key = data_blob_talloc(mem_ctx, NULL, 16);
798 if (!co_session_key.data) {
799 return NT_STATUS_NO_MEMORY;
803 MD5Update(&ctx, &pwbuf->data[516], 16);
804 MD5Update(&ctx, session_key.data, session_key.length);
805 MD5Final(co_session_key.data, &ctx);
807 arcfour_crypt_blob(pwbuf->data, 516, &co_session_key);
809 if (!decode_pw_buffer(pwbuf->data, new_pass, sizeof(new_pass),
810 &new_pass_len, STR_UNICODE)) {
811 DEBUG(3,("samr: failed to decode password buffer\n"));
812 return NT_STATUS_WRONG_PASSWORD;
815 /* set the password - samdb needs to know both the domain and user DNs,
816 so the domain password policy can be used */
817 return samdb_set_password(sam_ctx, mem_ctx,
818 account_dn, domain_dn,
821 False, /* This is a password set, not change */
822 True, /* run restriction tests */
827 set the user password using plaintext, obeying any user or domain
828 password restrictions
830 This wrapper function takes a SID as input, rather than a user DN,
831 and actually performs the password change
834 NTSTATUS samdb_set_password_sid(struct ldb_context *ctx, TALLOC_CTX *mem_ctx,
835 const struct dom_sid *user_sid,
836 const char *new_pass,
837 struct samr_Password *lmNewHash,
838 struct samr_Password *ntNewHash,
841 enum samr_RejectReason *reject_reason,
842 struct samr_DomInfo1 **_dominfo)
845 struct ldb_dn *user_dn;
846 struct ldb_message *msg;
849 ret = ldb_transaction_start(ctx);
851 DEBUG(1, ("Failed to start transaction: %s\n", ldb_errstring(ctx)));
852 return NT_STATUS_INTERNAL_DB_CORRUPTION;
855 user_dn = samdb_search_dn(ctx, mem_ctx, NULL,
856 "(&(objectSid=%s)(objectClass=user))",
857 ldap_encode_ndr_dom_sid(mem_ctx, user_sid));
859 ldb_transaction_cancel(ctx);
860 DEBUG(3, ("samdb_set_password_sid: SID %s not found in samdb, returning NO_SUCH_USER\n",
861 dom_sid_string(mem_ctx, user_sid)));
862 return NT_STATUS_NO_SUCH_USER;
865 msg = ldb_msg_new(mem_ctx);
867 ldb_transaction_cancel(ctx);
868 return NT_STATUS_NO_MEMORY;
871 msg->dn = ldb_dn_copy(msg, user_dn);
873 ldb_transaction_cancel(ctx);
874 return NT_STATUS_NO_MEMORY;
877 nt_status = samdb_set_password(ctx, mem_ctx,
880 lmNewHash, ntNewHash,
881 user_change, /* This is a password set, not change */
882 restrictions, /* run restriction tests */
883 reject_reason, _dominfo);
884 if (!NT_STATUS_IS_OK(nt_status)) {
885 ldb_transaction_cancel(ctx);
889 /* modify the samdb record */
890 ret = samdb_replace(ctx, mem_ctx, msg);
892 ldb_transaction_cancel(ctx);
893 return NT_STATUS_ACCESS_DENIED;
896 ret = ldb_transaction_commit(ctx);
898 DEBUG(0,("Failed to commit transaction to change password on %s: %s\n",
899 ldb_dn_linearize(mem_ctx, msg->dn),
900 ldb_errstring(ctx)));
901 return NT_STATUS_INTERNAL_DB_CORRUPTION;