2 Unix SMB/CIFS implementation.
4 samr server password set/change handling
6 Copyright (C) Andrew Tridgell 2004
8 This program is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 2 of the License, or
11 (at your option) any later version.
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
18 You should have received a copy of the GNU General Public License
19 along with this program; if not, write to the Free Software
20 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
24 #include "rpc_server/common/common.h"
25 #include "rpc_server/samr/dcesrv_samr.h"
28 samr_ChangePasswordUser
30 NTSTATUS samr_ChangePasswordUser(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
31 struct samr_ChangePasswordUser *r)
33 struct dcesrv_handle *h;
34 struct samr_account_state *a_state;
35 struct ldb_message **res, mod, *msg;
37 struct samr_Hash *lmPwdHash=NULL, *ntPwdHash=NULL;
38 struct samr_Hash new_lmPwdHash, new_ntPwdHash, checkHash;
39 NTSTATUS status = NT_STATUS_OK;
40 const char * const attrs[] = { "lmPwdHash", "ntPwdHash" , NULL };
42 DCESRV_PULL_HANDLE(h, r->in.handle, SAMR_HANDLE_USER);
46 /* fetch the old hashes */
47 ret = samdb_search(a_state->sam_ctx, mem_ctx, NULL, &res, attrs,
48 "dn=%s", a_state->account_dn);
50 return NT_STATUS_INTERNAL_DB_CORRUPTION;
54 /* basic sanity checking on parameters */
55 if (!r->in.lm_present || !r->in.nt_present ||
56 !r->in.old_lm_crypted || !r->in.new_lm_crypted ||
57 !r->in.old_nt_crypted || !r->in.new_nt_crypted) {
58 /* we should really handle a change with lm not
60 return NT_STATUS_INVALID_PARAMETER_MIX;
62 if (!r->in.cross1_present || !r->in.nt_cross) {
63 return NT_STATUS_NT_CROSS_ENCRYPTION_REQUIRED;
65 if (!r->in.cross2_present || !r->in.lm_cross) {
66 return NT_STATUS_LM_CROSS_ENCRYPTION_REQUIRED;
69 ret = samdb_result_hashes(mem_ctx, msg, "lmPwdHash", &lmPwdHash);
71 return NT_STATUS_WRONG_PASSWORD;
73 ret = samdb_result_hashes(mem_ctx, msg, "ntPwdHash", &ntPwdHash);
75 return NT_STATUS_WRONG_PASSWORD;
78 /* decrypt and check the new lm hash */
79 D_P16(lmPwdHash->hash, r->in.new_lm_crypted->hash, new_lmPwdHash.hash);
80 D_P16(new_lmPwdHash.hash, r->in.old_lm_crypted->hash, checkHash.hash);
81 if (memcmp(checkHash.hash, lmPwdHash->hash, 16) != 0) {
82 return NT_STATUS_WRONG_PASSWORD;
85 /* decrypt and check the new nt hash */
86 D_P16(ntPwdHash->hash, r->in.new_nt_crypted->hash, new_ntPwdHash.hash);
87 D_P16(new_ntPwdHash.hash, r->in.old_nt_crypted->hash, checkHash.hash);
88 if (memcmp(checkHash.hash, ntPwdHash->hash, 16) != 0) {
89 return NT_STATUS_WRONG_PASSWORD;
92 /* check the nt cross hash */
93 D_P16(lmPwdHash->hash, r->in.nt_cross->hash, checkHash.hash);
94 if (memcmp(checkHash.hash, new_ntPwdHash.hash, 16) != 0) {
95 return NT_STATUS_WRONG_PASSWORD;
98 /* check the lm cross hash */
99 D_P16(ntPwdHash->hash, r->in.lm_cross->hash, checkHash.hash);
100 if (memcmp(checkHash.hash, new_lmPwdHash.hash, 16) != 0) {
101 return NT_STATUS_WRONG_PASSWORD;
105 mod.dn = talloc_strdup(mem_ctx, a_state->account_dn);
107 return NT_STATUS_NO_MEMORY;
110 status = samdb_set_password(a_state->sam_ctx, mem_ctx,
111 a_state->account_dn, a_state->domain_state->domain_dn,
112 &mod, NULL, &new_lmPwdHash, &new_ntPwdHash,
114 if (!NT_STATUS_IS_OK(status)) {
118 /* modify the samdb record */
119 ret = samdb_replace(a_state->sam_ctx, mem_ctx, &mod);
121 return NT_STATUS_UNSUCCESSFUL;
128 samr_OemChangePasswordUser2
130 NTSTATUS samr_OemChangePasswordUser2(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
131 struct samr_OemChangePasswordUser2 *r)
135 uint32_t new_pass_len;
136 struct samr_CryptPassword *pwbuf = r->in.password;
138 const char *user_dn, *domain_dn;
140 struct ldb_message **res, mod;
141 const char * const attrs[] = { "objectSid", "lmPwdHash", NULL };
142 const char *domain_sid;
143 struct samr_Hash *lmPwdHash;
146 return NT_STATUS_WRONG_PASSWORD;
149 /* this call doesn't take a policy handle, so we need to open
150 the sam db from scratch */
151 sam_ctx = samdb_connect();
152 if (sam_ctx == NULL) {
153 return NT_STATUS_INVALID_SYSTEM_SERVICE;
156 /* we need the users dn and the domain dn (derived from the
157 user SID). We also need the current lm password hash in
158 order to decrypt the incoming password */
159 ret = samdb_search(sam_ctx,
160 mem_ctx, NULL, &res, attrs,
161 "(&(sAMAccountName=%s)(objectclass=user))",
162 r->in.account->name);
164 samdb_close(sam_ctx);
165 return NT_STATUS_NO_SUCH_USER;
168 user_dn = res[0]->dn;
170 ret = samdb_result_hashes(mem_ctx, res[0], "lmPwdHash", &lmPwdHash);
172 samdb_close(sam_ctx);
173 return NT_STATUS_WRONG_PASSWORD;
176 /* decrypt the password we have been given */
177 SamOEMhash(pwbuf->data, lmPwdHash->hash, 516);
179 if (!decode_pw_buffer(pwbuf->data, new_pass, sizeof(new_pass),
180 &new_pass_len, STR_ASCII)) {
181 DEBUG(3,("samr: failed to decode password buffer\n"));
182 samdb_close(sam_ctx);
183 return NT_STATUS_WRONG_PASSWORD;
186 /* work out the domain dn */
187 domain_sid = samdb_result_sid_prefix(mem_ctx, res[0], "objectSid");
188 if (domain_sid == NULL) {
189 samdb_close(sam_ctx);
190 return NT_STATUS_NO_SUCH_USER;
193 domain_dn = samdb_search_string(sam_ctx, mem_ctx, NULL, "dn",
194 "(objectSid=%s)", domain_sid);
196 samdb_close(sam_ctx);
197 return NT_STATUS_INTERNAL_DB_CORRUPTION;
202 mod.dn = talloc_strdup(mem_ctx, user_dn);
204 samdb_close(sam_ctx);
205 return NT_STATUS_NO_MEMORY;
208 /* set the password - samdb needs to know both the domain and user DNs,
209 so the domain password policy can be used */
210 status = samdb_set_password(sam_ctx, mem_ctx,
215 if (!NT_STATUS_IS_OK(status)) {
216 samdb_close(sam_ctx);
220 /* modify the samdb record */
221 ret = samdb_replace(sam_ctx, mem_ctx, &mod);
223 samdb_close(sam_ctx);
224 return NT_STATUS_UNSUCCESSFUL;
227 samdb_close(sam_ctx);
233 samr_ChangePasswordUser2
235 NTSTATUS samr_ChangePasswordUser2(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
236 struct samr_ChangePasswordUser2 *r)
238 DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
243 samr_ChangePasswordUser3
245 NTSTATUS samr_ChangePasswordUser3(struct dcesrv_call_state *dce_call,
247 struct samr_ChangePasswordUser3 *r)
251 uint32_t new_pass_len;
252 void *sam_ctx = NULL;
253 const char *user_dn, *domain_dn = NULL;
255 struct ldb_message **res, mod;
256 const char * const attrs[] = { "objectSid", "ntPwdHash", NULL };
257 const char * const dom_attrs[] = { "minPwdLength", "pwdHistoryLength",
258 "pwdProperties", "minPwdAge", "maxPwdAge",
260 const char *domain_sid;
261 struct samr_Hash *ntPwdHash;
262 struct samr_DomInfo1 *dominfo;
263 struct samr_ChangeReject *reject;
268 if (r->in.nt_password == NULL ||
269 r->in.nt_verifier == NULL) {
270 status = NT_STATUS_INVALID_PARAMETER;
274 /* this call doesn't take a policy handle, so we need to open
275 the sam db from scratch */
276 sam_ctx = samdb_connect();
277 if (sam_ctx == NULL) {
278 status = NT_STATUS_INVALID_SYSTEM_SERVICE;
282 /* we need the users dn and the domain dn (derived from the
283 user SID). We also need the current lm and nt password hashes
284 in order to decrypt the incoming passwords */
285 ret = samdb_search(sam_ctx,
286 mem_ctx, NULL, &res, attrs,
287 "(&(sAMAccountName=%s)(objectclass=user))",
288 r->in.account->name);
290 status = NT_STATUS_NO_SUCH_USER;
294 user_dn = res[0]->dn;
296 ret = samdb_result_hashes(mem_ctx, res[0], "ntPwdHash", &ntPwdHash);
298 status = NT_STATUS_WRONG_PASSWORD;
302 /* decrypt the password we have been given */
303 SamOEMhash(r->in.nt_password->data, ntPwdHash->hash, 516);
305 if (!decode_pw_buffer(r->in.nt_password->data, new_pass, sizeof(new_pass),
306 &new_pass_len, STR_UNICODE)) {
307 DEBUG(3,("samr: failed to decode password buffer\n"));
308 status = NT_STATUS_WRONG_PASSWORD;
312 /* work out the domain dn */
313 domain_sid = samdb_result_sid_prefix(mem_ctx, res[0], "objectSid");
314 if (domain_sid == NULL) {
315 status = NT_STATUS_NO_SUCH_DOMAIN;
319 domain_dn = samdb_search_string(sam_ctx, mem_ctx, NULL, "dn",
320 "(objectSid=%s)", domain_sid);
322 status = NT_STATUS_INTERNAL_DB_CORRUPTION;
328 mod.dn = talloc_strdup(mem_ctx, user_dn);
330 status = NT_STATUS_NO_MEMORY;
334 /* set the password - samdb needs to know both the domain and user DNs,
335 so the domain password policy can be used */
336 status = samdb_set_password(sam_ctx, mem_ctx,
341 if (!NT_STATUS_IS_OK(status)) {
345 /* modify the samdb record */
346 ret = samdb_replace(sam_ctx, mem_ctx, &mod);
348 status = NT_STATUS_UNSUCCESSFUL;
352 samdb_close(sam_ctx);
357 samdb_close(sam_ctx);
360 /* on failure we need to fill in the reject reasons */
361 dominfo = talloc_p(mem_ctx, struct samr_DomInfo1);
362 if (dominfo == NULL) {
363 return NT_STATUS_NO_MEMORY;
365 reject = talloc_p(mem_ctx, struct samr_ChangeReject);
366 if (reject == NULL) {
367 return NT_STATUS_NO_MEMORY;
370 ZERO_STRUCTP(dominfo);
371 ZERO_STRUCTP(reject);
373 reject->reason = reason;
375 r->out.dominfo = dominfo;
376 r->out.reject = reject;
382 ret = samdb_search(sam_ctx,
383 mem_ctx, NULL, &res, dom_attrs,
386 status = NT_STATUS_NO_SUCH_USER;
390 dominfo->min_pwd_len = samdb_result_uint(res[0], "minPwdLength", 0);
391 dominfo->password_properties = samdb_result_uint(res[0], "pwdProperties", 0);
392 dominfo->password_history = samdb_result_uint(res[0], "pwdHistoryLength", 0);
393 dominfo->max_password_age = samdb_result_int64(res[0], "maxPwdAge", 0);
394 dominfo->min_password_age = samdb_result_int64(res[0], "minPwdAge", 0);
403 check that a password is sufficiently complex
405 BOOL samdb_password_complexity_ok(const char *pass)
407 return check_password_quality(pass);
411 set the user password using plaintext, obeying any user or domain
412 password restrictions
414 note that this function doesn't actually store the result in the
415 database, it just fills in the "mod" structure with ldb modify
416 elements to setup the correct change when samdb_replace() is
417 called. This allows the caller to combine the change with other
418 changes (as is needed by some of the set user info levels)
420 NTSTATUS samdb_set_password(void *ctx, TALLOC_CTX *mem_ctx,
421 const char *user_dn, const char *domain_dn,
422 struct ldb_message *mod,
423 const char *new_pass,
424 struct samr_Hash *lmNewHash,
425 struct samr_Hash *ntNewHash,
427 uint32_t *reject_reason)
429 const char * const user_attrs[] = { "userAccountControl", "lmPwdHistory",
430 "ntPwdHistory", "unicodePwd",
431 "lmPwdHash", "ntPwdHash", "badPwdCount",
433 const char * const domain_attrs[] = { "pwdProperties", "pwdHistoryLength",
434 "maxPwdAge", "minPwdAge",
435 "minPwdLength", "pwdLastSet", NULL };
436 const char *unicodePwd;
439 uint_t minPwdLength, pwdProperties, pwdHistoryLength;
440 uint_t userAccountControl, badPwdCount;
441 struct samr_Hash *lmPwdHistory, *ntPwdHistory, lmPwdHash, ntPwdHash;
442 struct samr_Hash *new_lmPwdHistory, *new_ntPwdHistory;
443 struct samr_Hash local_lmNewHash, local_ntNewHash;
444 int lmPwdHistory_len, ntPwdHistory_len;
445 struct ldb_message **res;
447 time_t now = time(NULL);
451 /* we need to know the time to compute password age */
452 unix_to_nt_time(&now_nt, now);
454 /* pull all the user parameters */
455 count = samdb_search(ctx, mem_ctx, NULL, &res, user_attrs, "dn=%s", user_dn);
457 return NT_STATUS_INTERNAL_DB_CORRUPTION;
459 unicodePwd = samdb_result_string(res[0], "unicodePwd", NULL);
460 userAccountControl = samdb_result_uint(res[0], "userAccountControl", 0);
461 badPwdCount = samdb_result_uint(res[0], "badPwdCount", 0);
462 lmPwdHistory_len = samdb_result_hashes(mem_ctx, res[0],
463 "lmPwdHistory", &lmPwdHistory);
464 ntPwdHistory_len = samdb_result_hashes(mem_ctx, res[0],
465 "ntPwdHistory", &ntPwdHistory);
466 lmPwdHash = samdb_result_hash(res[0], "lmPwdHash");
467 ntPwdHash = samdb_result_hash(res[0], "ntPwdHash");
468 pwdLastSet = samdb_result_uint64(res[0], "pwdLastSet", 0);
470 /* pull the domain parameters */
471 count = samdb_search(ctx, mem_ctx, NULL, &res, domain_attrs, "dn=%s", domain_dn);
473 return NT_STATUS_INTERNAL_DB_CORRUPTION;
475 pwdProperties = samdb_result_uint(res[0], "pwdProperties", 0);
476 pwdHistoryLength = samdb_result_uint(res[0], "pwdHistoryLength", 0);
477 minPwdLength = samdb_result_uint(res[0], "minPwdLength", 0);
478 minPwdAge = samdb_result_int64(res[0], "minPwdAge", 0);
481 /* check the various password restrictions */
482 if (minPwdLength > str_charnum(new_pass)) {
484 *reject_reason = SAMR_REJECT_TOO_SHORT;
486 return NT_STATUS_PASSWORD_RESTRICTION;
489 /* possibly check password complexity */
490 if (pwdProperties & DOMAIN_PASSWORD_COMPLEX &&
491 !samdb_password_complexity_ok(new_pass)) {
493 *reject_reason = SAMR_REJECT_COMPLEXITY;
495 return NT_STATUS_PASSWORD_RESTRICTION;
498 /* compute the new nt and lm hashes */
499 if (E_deshash(new_pass, local_lmNewHash.hash)) {
500 lmNewHash = &local_lmNewHash;
502 E_md4hash(new_pass, local_ntNewHash.hash);
503 ntNewHash = &local_ntNewHash;
507 /* are all password changes disallowed? */
508 if (pwdProperties & DOMAIN_REFUSE_PASSWORD_CHANGE) {
510 *reject_reason = SAMR_REJECT_OTHER;
512 return NT_STATUS_PASSWORD_RESTRICTION;
515 /* can this user change password? */
516 if (userAccountControl & UF_PASSWD_CANT_CHANGE) {
518 *reject_reason = SAMR_REJECT_OTHER;
520 return NT_STATUS_PASSWORD_RESTRICTION;
523 /* yes, this is a minus. The ages are in negative 100nsec units! */
524 if (pwdLastSet - minPwdAge > now_nt) {
526 *reject_reason = SAMR_REJECT_OTHER;
528 return NT_STATUS_PASSWORD_RESTRICTION;
531 /* check the immediately past password */
532 if (pwdHistoryLength > 0) {
533 if (lmNewHash && memcmp(lmNewHash->hash, lmPwdHash.hash, 16) == 0) {
535 *reject_reason = SAMR_REJECT_COMPLEXITY;
537 return NT_STATUS_PASSWORD_RESTRICTION;
539 if (ntNewHash && memcmp(ntNewHash->hash, ntPwdHash.hash, 16) == 0) {
541 *reject_reason = SAMR_REJECT_COMPLEXITY;
543 return NT_STATUS_PASSWORD_RESTRICTION;
547 /* check the password history */
548 lmPwdHistory_len = MIN(lmPwdHistory_len, pwdHistoryLength);
549 ntPwdHistory_len = MIN(ntPwdHistory_len, pwdHistoryLength);
551 if (pwdHistoryLength > 0) {
552 if (unicodePwd && new_pass && strcmp(unicodePwd, new_pass) == 0) {
554 *reject_reason = SAMR_REJECT_COMPLEXITY;
556 return NT_STATUS_PASSWORD_RESTRICTION;
558 if (lmNewHash && memcmp(lmNewHash->hash, lmPwdHash.hash, 16) == 0) {
560 *reject_reason = SAMR_REJECT_COMPLEXITY;
562 return NT_STATUS_PASSWORD_RESTRICTION;
564 if (ntNewHash && memcmp(ntNewHash->hash, ntPwdHash.hash, 16) == 0) {
566 *reject_reason = SAMR_REJECT_COMPLEXITY;
568 return NT_STATUS_PASSWORD_RESTRICTION;
572 for (i=0; lmNewHash && i<lmPwdHistory_len;i++) {
573 if (memcmp(lmNewHash->hash, lmPwdHistory[i].hash, 16) == 0) {
575 *reject_reason = SAMR_REJECT_COMPLEXITY;
577 return NT_STATUS_PASSWORD_RESTRICTION;
580 for (i=0; ntNewHash && i<ntPwdHistory_len;i++) {
581 if (memcmp(ntNewHash->hash, ntPwdHistory[i].hash, 16) == 0) {
583 *reject_reason = SAMR_REJECT_COMPLEXITY;
585 return NT_STATUS_PASSWORD_RESTRICTION;
590 #define CHECK_RET(x) do { if (x != 0) return NT_STATUS_NO_MEMORY; } while(0)
592 /* the password is acceptable. Start forming the new fields */
594 CHECK_RET(samdb_msg_add_hash(ctx, mem_ctx, mod, "lmPwdHash", *lmNewHash));
596 CHECK_RET(samdb_msg_add_delete(ctx, mem_ctx, mod, "lmPwdHash"));
600 CHECK_RET(samdb_msg_add_hash(ctx, mem_ctx, mod, "ntPwdHash", *ntNewHash));
602 CHECK_RET(samdb_msg_add_delete(ctx, mem_ctx, mod, "ntPwdHash"));
605 if (new_pass && (pwdProperties & DOMAIN_PASSWORD_STORE_CLEARTEXT) &&
606 (userAccountControl & UF_ENCRYPTED_TEXT_PASSWORD_ALLOWED)) {
607 CHECK_RET(samdb_msg_add_string(ctx, mem_ctx, mod,
608 "unicodePwd", new_pass));
610 CHECK_RET(samdb_msg_add_delete(ctx, mem_ctx, mod, "unicodePwd"));
613 CHECK_RET(samdb_msg_add_uint64(ctx, mem_ctx, mod, "pwdLastSet", now_nt));
615 if (pwdHistoryLength == 0) {
616 CHECK_RET(samdb_msg_add_delete(ctx, mem_ctx, mod, "lmPwdHistory"));
617 CHECK_RET(samdb_msg_add_delete(ctx, mem_ctx, mod, "ntPwdHistory"));
621 /* store the password history */
622 new_lmPwdHistory = talloc_array_p(mem_ctx, struct samr_Hash,
624 if (!new_lmPwdHistory) {
625 return NT_STATUS_NO_MEMORY;
627 new_ntPwdHistory = talloc_array_p(mem_ctx, struct samr_Hash,
629 if (!new_ntPwdHistory) {
630 return NT_STATUS_NO_MEMORY;
632 for (i=0;i<MIN(pwdHistoryLength-1, lmPwdHistory_len);i++) {
633 new_lmPwdHistory[i+1] = lmPwdHistory[i];
635 for (i=0;i<MIN(pwdHistoryLength-1, ntPwdHistory_len);i++) {
636 new_ntPwdHistory[i+1] = ntPwdHistory[i];
639 /* Don't store 'long' passwords in the LM history,
640 but make sure to 'expire' one password off the other end */
642 new_lmPwdHistory[0] = *lmNewHash;
644 ZERO_STRUCT(new_lmPwdHistory[0]);
646 lmPwdHistory_len = MIN(lmPwdHistory_len + 1, pwdHistoryLength);
649 new_ntPwdHistory[0] = *ntNewHash;
651 ZERO_STRUCT(new_ntPwdHistory[0]);
653 ntPwdHistory_len = MIN(ntPwdHistory_len + 1, pwdHistoryLength);
655 CHECK_RET(samdb_msg_add_hashes(ctx, mem_ctx, mod,
660 CHECK_RET(samdb_msg_add_hashes(ctx, mem_ctx, mod,
668 set password via a samr_CryptPassword buffer
669 this will in the 'msg' with modify operations that will update the user
670 password when applied
672 NTSTATUS samr_set_password(struct dcesrv_call_state *dce_call,
674 const char *account_dn, const char *domain_dn,
676 struct ldb_message *msg,
677 struct samr_CryptPassword *pwbuf)
680 uint32_t new_pass_len;
681 DATA_BLOB session_key = dce_call->conn->session_key;
683 SamOEMhashBlob(pwbuf->data, 516, &session_key);
685 if (!decode_pw_buffer(pwbuf->data, new_pass, sizeof(new_pass),
686 &new_pass_len, STR_UNICODE)) {
687 DEBUG(3,("samr: failed to decode password buffer\n"));
688 return NT_STATUS_WRONG_PASSWORD;
691 /* set the password - samdb needs to know both the domain and user DNs,
692 so the domain password policy can be used */
693 return samdb_set_password(sam_ctx, mem_ctx,
694 account_dn, domain_dn,
697 False /* This is a password set, not change */,