r3804: Add more comparison tests in RPC-SAMSYNC.
[Samba/gebeck_regimport.git] / source4 / rpc_server / samr / samr_password.c
blob6f995081221e2b127d449709e8f444584e184f8f
1 /*
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.
23 #include "includes.h"
24 #include "librpc/gen_ndr/ndr_samr.h"
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"
30 #include "lib/ldb/include/ldb.h"
32 /*
33 samr_ChangePasswordUser
35 NTSTATUS samr_ChangePasswordUser(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
36 struct samr_ChangePasswordUser *r)
38 struct dcesrv_handle *h;
39 struct samr_account_state *a_state;
40 struct ldb_message **res, mod, *msg;
41 int ret;
42 struct samr_Password new_lmPwdHash, new_ntPwdHash, checkHash;
43 struct samr_Password *lm_pwd, *nt_pwd;
44 NTSTATUS status = NT_STATUS_OK;
45 const char * const attrs[] = { "lmPwdHash", "ntPwdHash" , "unicodePwd", NULL };
47 DCESRV_PULL_HANDLE(h, r->in.user_handle, SAMR_HANDLE_USER);
49 a_state = h->data;
51 /* fetch the old hashes */
52 ret = samdb_search(a_state->sam_ctx, mem_ctx, NULL, &res, attrs,
53 "dn=%s", a_state->account_dn);
54 if (ret != 1) {
55 return NT_STATUS_INTERNAL_DB_CORRUPTION;
57 msg = res[0];
59 /* basic sanity checking on parameters */
60 if (!r->in.lm_present || !r->in.nt_present ||
61 !r->in.old_lm_crypted || !r->in.new_lm_crypted ||
62 !r->in.old_nt_crypted || !r->in.new_nt_crypted) {
63 /* we should really handle a change with lm not
64 present */
65 return NT_STATUS_INVALID_PARAMETER_MIX;
67 if (!r->in.cross1_present || !r->in.nt_cross) {
68 return NT_STATUS_NT_CROSS_ENCRYPTION_REQUIRED;
70 if (!r->in.cross2_present || !r->in.lm_cross) {
71 return NT_STATUS_LM_CROSS_ENCRYPTION_REQUIRED;
74 status = samdb_result_passwords(mem_ctx, msg, &lm_pwd, &nt_pwd);
75 if (!NT_STATUS_IS_OK(status) || !lm_pwd || !nt_pwd) {
76 return NT_STATUS_WRONG_PASSWORD;
79 /* decrypt and check the new lm hash */
80 D_P16(lm_pwd->hash, r->in.new_lm_crypted->hash, new_lmPwdHash.hash);
81 D_P16(new_lmPwdHash.hash, r->in.old_lm_crypted->hash, checkHash.hash);
82 if (memcmp(checkHash.hash, lm_pwd, 16) != 0) {
83 return NT_STATUS_WRONG_PASSWORD;
86 /* decrypt and check the new nt hash */
87 D_P16(nt_pwd->hash, r->in.new_nt_crypted->hash, new_ntPwdHash.hash);
88 D_P16(new_ntPwdHash.hash, r->in.old_nt_crypted->hash, checkHash.hash);
89 if (memcmp(checkHash.hash, nt_pwd, 16) != 0) {
90 return NT_STATUS_WRONG_PASSWORD;
93 /* check the nt cross hash */
94 D_P16(lm_pwd->hash, r->in.nt_cross->hash, checkHash.hash);
95 if (memcmp(checkHash.hash, new_ntPwdHash.hash, 16) != 0) {
96 return NT_STATUS_WRONG_PASSWORD;
99 /* check the lm cross hash */
100 D_P16(nt_pwd->hash, r->in.lm_cross->hash, checkHash.hash);
101 if (memcmp(checkHash.hash, new_lmPwdHash.hash, 16) != 0) {
102 return NT_STATUS_WRONG_PASSWORD;
105 ZERO_STRUCT(mod);
106 mod.dn = talloc_strdup(mem_ctx, a_state->account_dn);
107 if (!mod.dn) {
108 return NT_STATUS_NO_MEMORY;
111 status = samdb_set_password(a_state->sam_ctx, mem_ctx,
112 a_state->account_dn, a_state->domain_state->domain_dn,
113 &mod, NULL, &new_lmPwdHash, &new_ntPwdHash,
114 True, NULL);
115 if (!NT_STATUS_IS_OK(status)) {
116 return status;
119 /* modify the samdb record */
120 ret = samdb_replace(a_state->sam_ctx, mem_ctx, &mod);
121 if (ret != 0) {
122 return NT_STATUS_UNSUCCESSFUL;
125 return NT_STATUS_OK;
129 samr_OemChangePasswordUser2
131 NTSTATUS samr_OemChangePasswordUser2(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
132 struct samr_OemChangePasswordUser2 *r)
134 NTSTATUS status;
135 char new_pass[512];
136 uint32_t new_pass_len;
137 struct samr_CryptPassword *pwbuf = r->in.password;
138 void *sam_ctx;
139 const char *user_dn, *domain_dn;
140 int ret;
141 struct ldb_message **res, mod;
142 const char * const attrs[] = { "objectSid", "lmPwdHash", "unicodePwd", NULL };
143 const char *domain_sid;
144 struct samr_Password *lm_pwd;
145 DATA_BLOB lm_pwd_blob;
147 if (pwbuf == NULL) {
148 return NT_STATUS_WRONG_PASSWORD;
151 /* this call doesn't take a policy handle, so we need to open
152 the sam db from scratch */
153 sam_ctx = samdb_connect(mem_ctx);
154 if (sam_ctx == NULL) {
155 return NT_STATUS_INVALID_SYSTEM_SERVICE;
158 /* we need the users dn and the domain dn (derived from the
159 user SID). We also need the current lm password hash in
160 order to decrypt the incoming password */
161 ret = samdb_search(sam_ctx,
162 mem_ctx, NULL, &res, attrs,
163 "(&(sAMAccountName=%s)(objectclass=user))",
164 r->in.account->string);
165 if (ret != 1) {
166 return NT_STATUS_NO_SUCH_USER;
169 user_dn = res[0]->dn;
171 status = samdb_result_passwords(mem_ctx, res[0], &lm_pwd, NULL);
172 if (!NT_STATUS_IS_OK(status) || !lm_pwd) {
173 return NT_STATUS_WRONG_PASSWORD;
176 /* decrypt the password we have been given */
177 lm_pwd_blob = data_blob(lm_pwd->hash, sizeof(lm_pwd->hash));
178 arcfour_crypt_blob(pwbuf->data, 516, &lm_pwd_blob);
179 data_blob_free(&lm_pwd_blob);
181 if (!decode_pw_buffer(pwbuf->data, new_pass, sizeof(new_pass),
182 &new_pass_len, STR_ASCII)) {
183 DEBUG(3,("samr: failed to decode password buffer\n"));
184 return NT_STATUS_WRONG_PASSWORD;
187 /* work out the domain dn */
188 domain_sid = samdb_result_sid_prefix(mem_ctx, res[0], "objectSid");
189 if (domain_sid == NULL) {
190 return NT_STATUS_NO_SUCH_USER;
193 domain_dn = samdb_search_string(sam_ctx, mem_ctx, NULL, "dn",
194 "(objectSid=%s)", domain_sid);
195 if (!domain_dn) {
196 return NT_STATUS_INTERNAL_DB_CORRUPTION;
200 ZERO_STRUCT(mod);
201 mod.dn = talloc_strdup(mem_ctx, user_dn);
202 if (!mod.dn) {
203 return NT_STATUS_NO_MEMORY;
206 /* set the password - samdb needs to know both the domain and user DNs,
207 so the domain password policy can be used */
208 status = samdb_set_password(sam_ctx, mem_ctx,
209 user_dn, domain_dn,
210 &mod, new_pass,
211 NULL, NULL,
212 True, NULL);
213 if (!NT_STATUS_IS_OK(status)) {
214 return status;
217 /* modify the samdb record */
218 ret = samdb_replace(sam_ctx, mem_ctx, &mod);
219 if (ret != 0) {
220 return NT_STATUS_UNSUCCESSFUL;
223 return NT_STATUS_OK;
228 samr_ChangePasswordUser3
230 NTSTATUS samr_ChangePasswordUser3(struct dcesrv_call_state *dce_call,
231 TALLOC_CTX *mem_ctx,
232 struct samr_ChangePasswordUser3 *r)
234 NTSTATUS status;
235 char new_pass[512];
236 uint32_t new_pass_len;
237 void *sam_ctx = NULL;
238 const char *user_dn, *domain_dn = NULL;
239 int ret;
240 struct ldb_message **res, mod;
241 const char * const attrs[] = { "objectSid", "ntPwdHash", "unicodePwd", NULL };
242 const char * const dom_attrs[] = { "minPwdLength", "pwdHistoryLength",
243 "pwdProperties", "minPwdAge", "maxPwdAge",
244 NULL };
245 const char *domain_sid;
246 struct samr_Password *nt_pwd;
247 DATA_BLOB nt_pwd_blob;
248 struct samr_DomInfo1 *dominfo;
249 struct samr_ChangeReject *reject;
250 uint32_t reason = 0;
252 ZERO_STRUCT(r->out);
254 if (r->in.nt_password == NULL ||
255 r->in.nt_verifier == NULL) {
256 status = NT_STATUS_INVALID_PARAMETER;
257 goto failed;
260 /* this call doesn't take a policy handle, so we need to open
261 the sam db from scratch */
262 sam_ctx = samdb_connect(mem_ctx);
263 if (sam_ctx == NULL) {
264 status = NT_STATUS_INVALID_SYSTEM_SERVICE;
265 goto failed;
268 /* we need the users dn and the domain dn (derived from the
269 user SID). We also need the current lm and nt password hashes
270 in order to decrypt the incoming passwords */
271 ret = samdb_search(sam_ctx,
272 mem_ctx, NULL, &res, attrs,
273 "(&(sAMAccountName=%s)(objectclass=user))",
274 r->in.account->string);
275 if (ret != 1) {
276 status = NT_STATUS_NO_SUCH_USER;
277 goto failed;
280 user_dn = res[0]->dn;
282 status = samdb_result_passwords(mem_ctx, res[0], NULL, &nt_pwd);
283 if (!NT_STATUS_IS_OK(status) ) {
284 goto failed;
287 if (!nt_pwd) {
288 status = NT_STATUS_WRONG_PASSWORD;
289 goto failed;
292 /* decrypt the password we have been given */
293 nt_pwd_blob = data_blob(nt_pwd->hash, sizeof(nt_pwd->hash));
294 arcfour_crypt_blob(r->in.nt_password->data, 516, &nt_pwd_blob);
295 data_blob_free(&nt_pwd_blob);
297 if (!decode_pw_buffer(r->in.nt_password->data, new_pass, sizeof(new_pass),
298 &new_pass_len, STR_UNICODE)) {
299 DEBUG(3,("samr: failed to decode password buffer\n"));
300 status = NT_STATUS_WRONG_PASSWORD;
301 goto failed;
304 /* work out the domain dn */
305 domain_sid = samdb_result_sid_prefix(mem_ctx, res[0], "objectSid");
306 if (domain_sid == NULL) {
307 status = NT_STATUS_NO_SUCH_DOMAIN;
308 goto failed;
311 domain_dn = samdb_search_string(sam_ctx, mem_ctx, NULL, "dn",
312 "(objectSid=%s)", domain_sid);
313 if (!domain_dn) {
314 status = NT_STATUS_INTERNAL_DB_CORRUPTION;
315 goto failed;
319 ZERO_STRUCT(mod);
320 mod.dn = talloc_strdup(mem_ctx, user_dn);
321 if (!mod.dn) {
322 status = NT_STATUS_NO_MEMORY;
323 goto failed;
326 /* set the password - samdb needs to know both the domain and user DNs,
327 so the domain password policy can be used */
328 status = samdb_set_password(sam_ctx, mem_ctx,
329 user_dn, domain_dn,
330 &mod, new_pass,
331 NULL, NULL,
332 True, &reason);
333 if (!NT_STATUS_IS_OK(status)) {
334 goto failed;
337 /* modify the samdb record */
338 ret = samdb_replace(sam_ctx, mem_ctx, &mod);
339 if (ret != 0) {
340 status = NT_STATUS_UNSUCCESSFUL;
341 goto failed;
344 return NT_STATUS_OK;
346 failed:
347 ret = samdb_search(sam_ctx,
348 mem_ctx, NULL, &res, dom_attrs,
349 "dn=%s", domain_dn);
351 if (ret != 1) {
352 return status;
355 /* on failure we need to fill in the reject reasons */
356 dominfo = talloc_p(mem_ctx, struct samr_DomInfo1);
357 if (dominfo == NULL) {
358 return status;
360 reject = talloc_p(mem_ctx, struct samr_ChangeReject);
361 if (reject == NULL) {
362 return status;
365 ZERO_STRUCTP(dominfo);
366 ZERO_STRUCTP(reject);
368 reject->reason = reason;
370 r->out.dominfo = dominfo;
371 r->out.reject = reject;
373 if (!domain_dn) {
374 return status;
377 dominfo->min_password_length = samdb_result_uint (res[0], "minPwdLength", 0);
378 dominfo->password_properties = samdb_result_uint (res[0], "pwdProperties", 0);
379 dominfo->password_history_length = samdb_result_uint (res[0], "pwdHistoryLength", 0);
380 dominfo->max_password_age = samdb_result_int64(res[0], "maxPwdAge", 0);
381 dominfo->min_password_age = samdb_result_int64(res[0], "minPwdAge", 0);
383 return status;
388 samr_ChangePasswordUser2
390 easy - just a subset of samr_ChangePasswordUser3
392 NTSTATUS samr_ChangePasswordUser2(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
393 struct samr_ChangePasswordUser2 *r)
395 struct samr_ChangePasswordUser3 r2;
397 r2.in.server = r->in.server;
398 r2.in.account = r->in.account;
399 r2.in.nt_password = r->in.nt_password;
400 r2.in.nt_verifier = r->in.nt_verifier;
401 r2.in.lm_change = r->in.lm_change;
402 r2.in.lm_password = r->in.lm_password;
403 r2.in.lm_verifier = r->in.lm_verifier;
404 r2.in.password3 = NULL;
406 return samr_ChangePasswordUser3(dce_call, mem_ctx, &r2);
411 check that a password is sufficiently complex
413 static BOOL samdb_password_complexity_ok(const char *pass)
415 return check_password_quality(pass);
419 set the user password using plaintext, obeying any user or domain
420 password restrictions
422 note that this function doesn't actually store the result in the
423 database, it just fills in the "mod" structure with ldb modify
424 elements to setup the correct change when samdb_replace() is
425 called. This allows the caller to combine the change with other
426 changes (as is needed by some of the set user info levels)
428 NTSTATUS samdb_set_password(void *ctx, TALLOC_CTX *mem_ctx,
429 const char *user_dn, const char *domain_dn,
430 struct ldb_message *mod,
431 const char *new_pass,
432 struct samr_Password *lmNewHash,
433 struct samr_Password *ntNewHash,
434 BOOL user_change,
435 uint32_t *reject_reason)
437 const char * const user_attrs[] = { "userAccountControl", "lmPwdHistory",
438 "ntPwdHistory", "unicodePwd",
439 "lmPwdHash", "ntPwdHash", "badPwdCount",
440 NULL };
441 const char * const domain_attrs[] = { "pwdProperties", "pwdHistoryLength",
442 "maxPwdAge", "minPwdAge",
443 "minPwdLength", "pwdLastSet", NULL };
444 const char *unicodePwd;
445 NTTIME pwdLastSet;
446 int64_t minPwdAge;
447 uint_t minPwdLength, pwdProperties, pwdHistoryLength;
448 uint_t userAccountControl, badPwdCount;
449 struct samr_Password *lmPwdHistory, *ntPwdHistory, lmPwdHash, ntPwdHash;
450 struct samr_Password *new_lmPwdHistory, *new_ntPwdHistory;
451 struct samr_Password local_lmNewHash, local_ntNewHash;
452 int lmPwdHistory_len, ntPwdHistory_len;
453 struct ldb_message **res;
454 int count;
455 time_t now = time(NULL);
456 NTTIME now_nt;
457 int i;
459 /* we need to know the time to compute password age */
460 unix_to_nt_time(&now_nt, now);
462 /* pull all the user parameters */
463 count = samdb_search(ctx, mem_ctx, NULL, &res, user_attrs, "dn=%s", user_dn);
464 if (count != 1) {
465 return NT_STATUS_INTERNAL_DB_CORRUPTION;
467 unicodePwd = samdb_result_string(res[0], "unicodePwd", NULL);
468 userAccountControl = samdb_result_uint(res[0], "userAccountControl", 0);
469 badPwdCount = samdb_result_uint(res[0], "badPwdCount", 0);
470 lmPwdHistory_len = samdb_result_hashes(mem_ctx, res[0],
471 "lmPwdHistory", &lmPwdHistory);
472 ntPwdHistory_len = samdb_result_hashes(mem_ctx, res[0],
473 "ntPwdHistory", &ntPwdHistory);
474 lmPwdHash = samdb_result_hash(res[0], "lmPwdHash");
475 ntPwdHash = samdb_result_hash(res[0], "ntPwdHash");
476 pwdLastSet = samdb_result_uint64(res[0], "pwdLastSet", 0);
478 /* pull the domain parameters */
479 count = samdb_search(ctx, mem_ctx, NULL, &res, domain_attrs, "dn=%s", domain_dn);
480 if (count != 1) {
481 return NT_STATUS_INTERNAL_DB_CORRUPTION;
483 pwdProperties = samdb_result_uint(res[0], "pwdProperties", 0);
484 pwdHistoryLength = samdb_result_uint(res[0], "pwdHistoryLength", 0);
485 minPwdLength = samdb_result_uint(res[0], "minPwdLength", 0);
486 minPwdAge = samdb_result_int64(res[0], "minPwdAge", 0);
488 if (new_pass) {
489 /* check the various password restrictions */
490 if (minPwdLength > strlen_m(new_pass)) {
491 if (reject_reason) {
492 *reject_reason = SAMR_REJECT_TOO_SHORT;
494 return NT_STATUS_PASSWORD_RESTRICTION;
497 /* possibly check password complexity */
498 if (pwdProperties & DOMAIN_PASSWORD_COMPLEX &&
499 !samdb_password_complexity_ok(new_pass)) {
500 if (reject_reason) {
501 *reject_reason = SAMR_REJECT_COMPLEXITY;
503 return NT_STATUS_PASSWORD_RESTRICTION;
506 /* compute the new nt and lm hashes */
507 if (E_deshash(new_pass, local_lmNewHash.hash)) {
508 lmNewHash = &local_lmNewHash;
510 E_md4hash(new_pass, local_ntNewHash.hash);
511 ntNewHash = &local_ntNewHash;
514 if (user_change) {
515 /* are all password changes disallowed? */
516 if (pwdProperties & DOMAIN_REFUSE_PASSWORD_CHANGE) {
517 if (reject_reason) {
518 *reject_reason = SAMR_REJECT_OTHER;
520 return NT_STATUS_PASSWORD_RESTRICTION;
523 /* can this user change password? */
524 if (userAccountControl & UF_PASSWD_CANT_CHANGE) {
525 if (reject_reason) {
526 *reject_reason = SAMR_REJECT_OTHER;
528 return NT_STATUS_PASSWORD_RESTRICTION;
531 /* yes, this is a minus. The ages are in negative 100nsec units! */
532 if (pwdLastSet - minPwdAge > now_nt) {
533 if (reject_reason) {
534 *reject_reason = SAMR_REJECT_OTHER;
536 return NT_STATUS_PASSWORD_RESTRICTION;
539 /* check the immediately past password */
540 if (pwdHistoryLength > 0) {
541 if (lmNewHash && memcmp(lmNewHash->hash, lmPwdHash.hash, 16) == 0) {
542 if (reject_reason) {
543 *reject_reason = SAMR_REJECT_COMPLEXITY;
545 return NT_STATUS_PASSWORD_RESTRICTION;
547 if (ntNewHash && memcmp(ntNewHash->hash, ntPwdHash.hash, 16) == 0) {
548 if (reject_reason) {
549 *reject_reason = SAMR_REJECT_COMPLEXITY;
551 return NT_STATUS_PASSWORD_RESTRICTION;
555 /* check the password history */
556 lmPwdHistory_len = MIN(lmPwdHistory_len, pwdHistoryLength);
557 ntPwdHistory_len = MIN(ntPwdHistory_len, pwdHistoryLength);
559 if (pwdHistoryLength > 0) {
560 if (unicodePwd && new_pass && strcmp(unicodePwd, new_pass) == 0) {
561 if (reject_reason) {
562 *reject_reason = SAMR_REJECT_COMPLEXITY;
564 return NT_STATUS_PASSWORD_RESTRICTION;
566 if (lmNewHash && memcmp(lmNewHash->hash, lmPwdHash.hash, 16) == 0) {
567 if (reject_reason) {
568 *reject_reason = SAMR_REJECT_COMPLEXITY;
570 return NT_STATUS_PASSWORD_RESTRICTION;
572 if (ntNewHash && memcmp(ntNewHash->hash, ntPwdHash.hash, 16) == 0) {
573 if (reject_reason) {
574 *reject_reason = SAMR_REJECT_COMPLEXITY;
576 return NT_STATUS_PASSWORD_RESTRICTION;
580 for (i=0; lmNewHash && i<lmPwdHistory_len;i++) {
581 if (memcmp(lmNewHash->hash, lmPwdHistory[i].hash, 16) == 0) {
582 if (reject_reason) {
583 *reject_reason = SAMR_REJECT_COMPLEXITY;
585 return NT_STATUS_PASSWORD_RESTRICTION;
588 for (i=0; ntNewHash && i<ntPwdHistory_len;i++) {
589 if (memcmp(ntNewHash->hash, ntPwdHistory[i].hash, 16) == 0) {
590 if (reject_reason) {
591 *reject_reason = SAMR_REJECT_COMPLEXITY;
593 return NT_STATUS_PASSWORD_RESTRICTION;
598 #define CHECK_RET(x) do { if (x != 0) return NT_STATUS_NO_MEMORY; } while(0)
600 /* the password is acceptable. Start forming the new fields */
601 if (lmNewHash) {
602 CHECK_RET(samdb_msg_add_hash(ctx, mem_ctx, mod, "lmPwdHash", *lmNewHash));
603 } else {
604 CHECK_RET(samdb_msg_add_delete(ctx, mem_ctx, mod, "lmPwdHash"));
607 if (ntNewHash) {
608 CHECK_RET(samdb_msg_add_hash(ctx, mem_ctx, mod, "ntPwdHash", *ntNewHash));
609 } else {
610 CHECK_RET(samdb_msg_add_delete(ctx, mem_ctx, mod, "ntPwdHash"));
613 if (new_pass && (pwdProperties & DOMAIN_PASSWORD_STORE_CLEARTEXT) &&
614 (userAccountControl & UF_ENCRYPTED_TEXT_PASSWORD_ALLOWED)) {
615 CHECK_RET(samdb_msg_add_string(ctx, mem_ctx, mod,
616 "unicodePwd", new_pass));
617 } else {
618 CHECK_RET(samdb_msg_add_delete(ctx, mem_ctx, mod, "unicodePwd"));
621 CHECK_RET(samdb_msg_add_uint64(ctx, mem_ctx, mod, "pwdLastSet", now_nt));
623 if (pwdHistoryLength == 0) {
624 CHECK_RET(samdb_msg_add_delete(ctx, mem_ctx, mod, "lmPwdHistory"));
625 CHECK_RET(samdb_msg_add_delete(ctx, mem_ctx, mod, "ntPwdHistory"));
626 return NT_STATUS_OK;
629 /* store the password history */
630 new_lmPwdHistory = talloc_array_p(mem_ctx, struct samr_Password,
631 pwdHistoryLength);
632 if (!new_lmPwdHistory) {
633 return NT_STATUS_NO_MEMORY;
635 new_ntPwdHistory = talloc_array_p(mem_ctx, struct samr_Password,
636 pwdHistoryLength);
637 if (!new_ntPwdHistory) {
638 return NT_STATUS_NO_MEMORY;
640 for (i=0;i<MIN(pwdHistoryLength-1, lmPwdHistory_len);i++) {
641 new_lmPwdHistory[i+1] = lmPwdHistory[i];
643 for (i=0;i<MIN(pwdHistoryLength-1, ntPwdHistory_len);i++) {
644 new_ntPwdHistory[i+1] = ntPwdHistory[i];
647 /* Don't store 'long' passwords in the LM history,
648 but make sure to 'expire' one password off the other end */
649 if (lmNewHash) {
650 new_lmPwdHistory[0] = *lmNewHash;
651 } else {
652 ZERO_STRUCT(new_lmPwdHistory[0]);
654 lmPwdHistory_len = MIN(lmPwdHistory_len + 1, pwdHistoryLength);
656 if (ntNewHash) {
657 new_ntPwdHistory[0] = *ntNewHash;
658 } else {
659 ZERO_STRUCT(new_ntPwdHistory[0]);
661 ntPwdHistory_len = MIN(ntPwdHistory_len + 1, pwdHistoryLength);
663 CHECK_RET(samdb_msg_add_hashes(ctx, mem_ctx, mod,
664 "lmPwdHistory",
665 new_lmPwdHistory,
666 lmPwdHistory_len));
668 CHECK_RET(samdb_msg_add_hashes(ctx, mem_ctx, mod,
669 "ntPwdHistory",
670 new_ntPwdHistory,
671 ntPwdHistory_len));
672 return NT_STATUS_OK;
676 set password via a samr_CryptPassword buffer
677 this will in the 'msg' with modify operations that will update the user
678 password when applied
680 NTSTATUS samr_set_password(struct dcesrv_call_state *dce_call,
681 void *sam_ctx,
682 const char *account_dn, const char *domain_dn,
683 TALLOC_CTX *mem_ctx,
684 struct ldb_message *msg,
685 struct samr_CryptPassword *pwbuf)
687 NTSTATUS nt_status;
688 char new_pass[512];
689 uint32_t new_pass_len;
690 DATA_BLOB session_key = data_blob(NULL, 0);
692 nt_status = dcesrv_fetch_session_key(dce_call->conn, &session_key);
693 if (!NT_STATUS_IS_OK(nt_status)) {
694 return nt_status;
697 arcfour_crypt_blob(pwbuf->data, 516, &session_key);
699 if (!decode_pw_buffer(pwbuf->data, new_pass, sizeof(new_pass),
700 &new_pass_len, STR_UNICODE)) {
701 DEBUG(3,("samr: failed to decode password buffer\n"));
702 return NT_STATUS_WRONG_PASSWORD;
705 /* set the password - samdb needs to know both the domain and user DNs,
706 so the domain password policy can be used */
707 return samdb_set_password(sam_ctx, mem_ctx,
708 account_dn, domain_dn,
709 msg, new_pass,
710 NULL, NULL,
711 False /* This is a password set, not change */,
712 NULL);
717 set password via a samr_CryptPasswordEx buffer
718 this will in the 'msg' with modify operations that will update the user
719 password when applied
721 NTSTATUS samr_set_password_ex(struct dcesrv_call_state *dce_call,
722 void *sam_ctx,
723 const char *account_dn, const char *domain_dn,
724 TALLOC_CTX *mem_ctx,
725 struct ldb_message *msg,
726 struct samr_CryptPasswordEx *pwbuf)
728 NTSTATUS nt_status;
729 char new_pass[512];
730 uint32_t new_pass_len;
731 DATA_BLOB co_session_key;
732 DATA_BLOB session_key = data_blob(NULL, 0);
733 struct MD5Context ctx;
735 nt_status = dcesrv_fetch_session_key(dce_call->conn, &session_key);
736 if (!NT_STATUS_IS_OK(nt_status)) {
737 return nt_status;
740 co_session_key = data_blob_talloc(mem_ctx, NULL, 16);
741 if (!co_session_key.data) {
742 return NT_STATUS_NO_MEMORY;
745 MD5Init(&ctx);
746 MD5Update(&ctx, &pwbuf->data[516], 16);
747 MD5Update(&ctx, session_key.data, session_key.length);
748 MD5Final(co_session_key.data, &ctx);
750 arcfour_crypt_blob(pwbuf->data, 516, &co_session_key);
752 if (!decode_pw_buffer(pwbuf->data, new_pass, sizeof(new_pass),
753 &new_pass_len, STR_UNICODE)) {
754 DEBUG(3,("samr: failed to decode password buffer\n"));
755 return NT_STATUS_WRONG_PASSWORD;
758 /* set the password - samdb needs to know both the domain and user DNs,
759 so the domain password policy can be used */
760 return samdb_set_password(sam_ctx, mem_ctx,
761 account_dn, domain_dn,
762 msg, new_pass,
763 NULL, NULL,
764 False,
765 NULL);