r5988: Fix the -P option (use machine account credentials) to use the Samba4
[Samba/gebeck_regimport.git] / source4 / rpc_server / samr / samr_password.c
blobf5390cc1d53293dba9c727002d8c9af8cec6fc42
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"
31 #include "ads.h"
33 /*
34 samr_ChangePasswordUser
36 NTSTATUS samr_ChangePasswordUser(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
37 struct samr_ChangePasswordUser *r)
39 struct dcesrv_handle *h;
40 struct samr_account_state *a_state;
41 struct ldb_message **res, *msg;
42 int ret;
43 struct samr_Password new_lmPwdHash, new_ntPwdHash, checkHash;
44 struct samr_Password *lm_pwd, *nt_pwd;
45 NTSTATUS status = NT_STATUS_OK;
46 const char * const attrs[] = { "lmPwdHash", "ntPwdHash" , "unicodePwd", NULL };
48 DCESRV_PULL_HANDLE(h, r->in.user_handle, SAMR_HANDLE_USER);
50 a_state = h->data;
52 /* fetch the old hashes */
53 ret = gendb_search(a_state->sam_ctx, mem_ctx, NULL, &res, attrs,
54 "dn=%s", a_state->account_dn);
55 if (ret != 1) {
56 return NT_STATUS_INTERNAL_DB_CORRUPTION;
58 msg = res[0];
60 /* basic sanity checking on parameters */
61 if (!r->in.lm_present || !r->in.nt_present ||
62 !r->in.old_lm_crypted || !r->in.new_lm_crypted ||
63 !r->in.old_nt_crypted || !r->in.new_nt_crypted) {
64 /* we should really handle a change with lm not
65 present */
66 return NT_STATUS_INVALID_PARAMETER_MIX;
68 if (!r->in.cross1_present || !r->in.nt_cross) {
69 return NT_STATUS_NT_CROSS_ENCRYPTION_REQUIRED;
71 if (!r->in.cross2_present || !r->in.lm_cross) {
72 return NT_STATUS_LM_CROSS_ENCRYPTION_REQUIRED;
75 status = samdb_result_passwords(mem_ctx, msg, &lm_pwd, &nt_pwd);
76 if (!NT_STATUS_IS_OK(status) || !lm_pwd || !nt_pwd) {
77 return NT_STATUS_WRONG_PASSWORD;
80 /* decrypt and check the new lm hash */
81 D_P16(lm_pwd->hash, r->in.new_lm_crypted->hash, new_lmPwdHash.hash);
82 D_P16(new_lmPwdHash.hash, r->in.old_lm_crypted->hash, checkHash.hash);
83 if (memcmp(checkHash.hash, lm_pwd, 16) != 0) {
84 return NT_STATUS_WRONG_PASSWORD;
87 /* decrypt and check the new nt hash */
88 D_P16(nt_pwd->hash, r->in.new_nt_crypted->hash, new_ntPwdHash.hash);
89 D_P16(new_ntPwdHash.hash, r->in.old_nt_crypted->hash, checkHash.hash);
90 if (memcmp(checkHash.hash, nt_pwd, 16) != 0) {
91 return NT_STATUS_WRONG_PASSWORD;
94 /* check the nt cross hash */
95 D_P16(lm_pwd->hash, r->in.nt_cross->hash, checkHash.hash);
96 if (memcmp(checkHash.hash, new_ntPwdHash.hash, 16) != 0) {
97 return NT_STATUS_WRONG_PASSWORD;
100 /* check the lm cross hash */
101 D_P16(nt_pwd->hash, r->in.lm_cross->hash, checkHash.hash);
102 if (memcmp(checkHash.hash, new_lmPwdHash.hash, 16) != 0) {
103 return NT_STATUS_WRONG_PASSWORD;
106 msg = ldb_msg_new(mem_ctx);
107 if (msg == NULL) {
108 return NT_STATUS_NO_MEMORY;
111 msg->dn = talloc_strdup(msg, a_state->account_dn);
112 if (!msg->dn) {
113 return NT_STATUS_NO_MEMORY;
116 status = samdb_set_password(a_state->sam_ctx, mem_ctx,
117 a_state->account_dn, a_state->domain_state->domain_dn,
118 msg, NULL, &new_lmPwdHash, &new_ntPwdHash,
119 True, /* this is a user password change */
120 True, /* run restriction tests */
121 NULL);
122 if (!NT_STATUS_IS_OK(status)) {
123 return status;
126 /* modify the samdb record */
127 ret = samdb_replace(a_state->sam_ctx, mem_ctx, msg);
128 if (ret != 0) {
129 return NT_STATUS_UNSUCCESSFUL;
132 return NT_STATUS_OK;
136 samr_OemChangePasswordUser2
138 NTSTATUS samr_OemChangePasswordUser2(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
139 struct samr_OemChangePasswordUser2 *r)
141 NTSTATUS status;
142 char new_pass[512];
143 uint32_t new_pass_len;
144 struct samr_CryptPassword *pwbuf = r->in.password;
145 void *sam_ctx;
146 const char *user_dn, *domain_dn;
147 int ret;
148 struct ldb_message **res, *mod;
149 const char * const attrs[] = { "objectSid", "lmPwdHash", "unicodePwd", NULL };
150 const char *domain_sid;
151 struct samr_Password *lm_pwd;
152 DATA_BLOB lm_pwd_blob;
153 uint8_t new_lm_hash[16];
154 struct samr_Password lm_verifier;
156 if (pwbuf == NULL) {
157 return NT_STATUS_WRONG_PASSWORD;
160 /* this call doesn't take a policy handle, so we need to open
161 the sam db from scratch */
162 sam_ctx = samdb_connect(mem_ctx);
163 if (sam_ctx == NULL) {
164 return NT_STATUS_INVALID_SYSTEM_SERVICE;
167 /* we need the users dn and the domain dn (derived from the
168 user SID). We also need the current lm password hash in
169 order to decrypt the incoming password */
170 ret = gendb_search(sam_ctx,
171 mem_ctx, NULL, &res, attrs,
172 "(&(sAMAccountName=%s)(objectclass=user))",
173 r->in.account->string);
174 if (ret != 1) {
175 return NT_STATUS_NO_SUCH_USER;
178 user_dn = res[0]->dn;
180 status = samdb_result_passwords(mem_ctx, res[0], &lm_pwd, NULL);
181 if (!NT_STATUS_IS_OK(status) || !lm_pwd) {
182 return NT_STATUS_WRONG_PASSWORD;
185 /* decrypt the password we have been given */
186 lm_pwd_blob = data_blob(lm_pwd->hash, sizeof(lm_pwd->hash));
187 arcfour_crypt_blob(pwbuf->data, 516, &lm_pwd_blob);
188 data_blob_free(&lm_pwd_blob);
190 if (!decode_pw_buffer(pwbuf->data, new_pass, sizeof(new_pass),
191 &new_pass_len, STR_ASCII)) {
192 DEBUG(3,("samr: failed to decode password buffer\n"));
193 return NT_STATUS_WRONG_PASSWORD;
196 /* check LM verifier */
197 if (lm_pwd == NULL || r->in.hash == NULL) {
198 return NT_STATUS_WRONG_PASSWORD;
201 E_deshash(new_pass, new_lm_hash);
202 E_old_pw_hash(new_lm_hash, lm_pwd->hash, lm_verifier.hash);
203 if (memcmp(lm_verifier.hash, r->in.hash->hash, 16) != 0) {
204 return NT_STATUS_WRONG_PASSWORD;
207 /* work out the domain dn */
208 domain_sid = samdb_result_sid_prefix(mem_ctx, res[0], "objectSid");
209 if (domain_sid == NULL) {
210 return NT_STATUS_NO_SUCH_USER;
213 domain_dn = samdb_search_string(sam_ctx, mem_ctx, NULL, "dn",
214 "(objectSid=%s)", domain_sid);
215 if (!domain_dn) {
216 return NT_STATUS_INTERNAL_DB_CORRUPTION;
219 mod = ldb_msg_new(mem_ctx);
220 if (mod == NULL) {
221 return NT_STATUS_NO_MEMORY;
224 mod->dn = talloc_strdup(mod, user_dn);
225 if (!mod->dn) {
226 return NT_STATUS_NO_MEMORY;
229 /* set the password - samdb needs to know both the domain and user DNs,
230 so the domain password policy can be used */
231 status = samdb_set_password(sam_ctx, mem_ctx,
232 user_dn, domain_dn,
233 mod, new_pass,
234 NULL, NULL,
235 True, /* this is a user password change */
236 True, /* run restriction tests */
237 NULL);
238 if (!NT_STATUS_IS_OK(status)) {
239 return status;
242 /* modify the samdb record */
243 ret = samdb_replace(sam_ctx, mem_ctx, mod);
244 if (ret != 0) {
245 return NT_STATUS_UNSUCCESSFUL;
248 return NT_STATUS_OK;
253 samr_ChangePasswordUser3
255 NTSTATUS samr_ChangePasswordUser3(struct dcesrv_call_state *dce_call,
256 TALLOC_CTX *mem_ctx,
257 struct samr_ChangePasswordUser3 *r)
259 NTSTATUS status;
260 char new_pass[512];
261 uint32_t new_pass_len;
262 void *sam_ctx = NULL;
263 const char *user_dn, *domain_dn = NULL;
264 int ret;
265 struct ldb_message **res, *mod;
266 const char * const attrs[] = { "objectSid", "ntPwdHash", "lmPwdHash", "unicodePwd", NULL };
267 const char * const dom_attrs[] = { "minPwdLength", "pwdHistoryLength",
268 "pwdProperties", "minPwdAge", "maxPwdAge",
269 NULL };
270 const char *domain_sid;
271 struct samr_Password *nt_pwd, *lm_pwd;
272 DATA_BLOB nt_pwd_blob;
273 struct samr_DomInfo1 *dominfo;
274 struct samr_ChangeReject *reject;
275 uint32_t reason = 0;
276 uint8_t new_nt_hash[16], new_lm_hash[16];
277 struct samr_Password nt_verifier, lm_verifier;
279 ZERO_STRUCT(r->out);
281 if (r->in.nt_password == NULL ||
282 r->in.nt_verifier == NULL) {
283 status = NT_STATUS_INVALID_PARAMETER;
284 goto failed;
287 /* this call doesn't take a policy handle, so we need to open
288 the sam db from scratch */
289 sam_ctx = samdb_connect(mem_ctx);
290 if (sam_ctx == NULL) {
291 status = NT_STATUS_INVALID_SYSTEM_SERVICE;
292 goto failed;
295 /* we need the users dn and the domain dn (derived from the
296 user SID). We also need the current lm and nt password hashes
297 in order to decrypt the incoming passwords */
298 ret = gendb_search(sam_ctx,
299 mem_ctx, NULL, &res, attrs,
300 "(&(sAMAccountName=%s)(objectclass=user))",
301 r->in.account->string);
302 if (ret != 1) {
303 status = NT_STATUS_NO_SUCH_USER;
304 goto failed;
307 user_dn = res[0]->dn;
309 status = samdb_result_passwords(mem_ctx, res[0], &lm_pwd, &nt_pwd);
310 if (!NT_STATUS_IS_OK(status) ) {
311 goto failed;
314 if (!nt_pwd) {
315 status = NT_STATUS_WRONG_PASSWORD;
316 goto failed;
319 /* decrypt the password we have been given */
320 nt_pwd_blob = data_blob(nt_pwd->hash, sizeof(nt_pwd->hash));
321 arcfour_crypt_blob(r->in.nt_password->data, 516, &nt_pwd_blob);
322 data_blob_free(&nt_pwd_blob);
324 if (!decode_pw_buffer(r->in.nt_password->data, new_pass, sizeof(new_pass),
325 &new_pass_len, STR_UNICODE)) {
326 DEBUG(3,("samr: failed to decode password buffer\n"));
327 status = NT_STATUS_WRONG_PASSWORD;
328 goto failed;
331 if (r->in.nt_verifier == NULL) {
332 status = NT_STATUS_WRONG_PASSWORD;
333 goto failed;
336 /* check NT verifier */
337 E_md4hash(new_pass, new_nt_hash);
338 E_old_pw_hash(new_nt_hash, nt_pwd->hash, nt_verifier.hash);
339 if (memcmp(nt_verifier.hash, r->in.nt_verifier->hash, 16) != 0) {
340 status = NT_STATUS_WRONG_PASSWORD;
341 goto failed;
344 /* check LM verifier */
345 if (lm_pwd && r->in.lm_verifier != NULL) {
346 E_deshash(new_pass, new_lm_hash);
347 E_old_pw_hash(new_nt_hash, lm_pwd->hash, lm_verifier.hash);
348 if (memcmp(lm_verifier.hash, r->in.lm_verifier->hash, 16) != 0) {
349 status = NT_STATUS_WRONG_PASSWORD;
350 goto failed;
355 /* work out the domain dn */
356 domain_sid = samdb_result_sid_prefix(mem_ctx, res[0], "objectSid");
357 if (domain_sid == NULL) {
358 status = NT_STATUS_NO_SUCH_DOMAIN;
359 goto failed;
362 domain_dn = samdb_search_string(sam_ctx, mem_ctx, NULL, "dn",
363 "(objectSid=%s)", domain_sid);
364 if (!domain_dn) {
365 status = NT_STATUS_INTERNAL_DB_CORRUPTION;
366 goto failed;
369 mod = ldb_msg_new(mem_ctx);
370 if (mod == NULL) {
371 return NT_STATUS_NO_MEMORY;
374 mod->dn = talloc_strdup(mod, user_dn);
375 if (!mod->dn) {
376 status = NT_STATUS_NO_MEMORY;
377 goto failed;
380 /* set the password - samdb needs to know both the domain and user DNs,
381 so the domain password policy can be used */
382 status = samdb_set_password(sam_ctx, mem_ctx,
383 user_dn, domain_dn,
384 mod, new_pass,
385 NULL, NULL,
386 True, /* this is a user password change */
387 True, /* run restriction tests */
388 &reason);
389 if (!NT_STATUS_IS_OK(status)) {
390 goto failed;
393 /* modify the samdb record */
394 ret = samdb_replace(sam_ctx, mem_ctx, mod);
395 if (ret != 0) {
396 status = NT_STATUS_UNSUCCESSFUL;
397 goto failed;
400 return NT_STATUS_OK;
402 failed:
403 ret = gendb_search(sam_ctx,
404 mem_ctx, NULL, &res, dom_attrs,
405 "dn=%s", domain_dn);
407 if (ret != 1) {
408 return status;
411 /* on failure we need to fill in the reject reasons */
412 dominfo = talloc(mem_ctx, struct samr_DomInfo1);
413 if (dominfo == NULL) {
414 return status;
416 reject = talloc(mem_ctx, struct samr_ChangeReject);
417 if (reject == NULL) {
418 return status;
421 ZERO_STRUCTP(dominfo);
422 ZERO_STRUCTP(reject);
424 reject->reason = reason;
426 r->out.dominfo = dominfo;
427 r->out.reject = reject;
429 if (!domain_dn) {
430 return status;
433 dominfo->min_password_length = samdb_result_uint (res[0], "minPwdLength", 0);
434 dominfo->password_properties = samdb_result_uint (res[0], "pwdProperties", 0);
435 dominfo->password_history_length = samdb_result_uint (res[0], "pwdHistoryLength", 0);
436 dominfo->max_password_age = samdb_result_int64(res[0], "maxPwdAge", 0);
437 dominfo->min_password_age = samdb_result_int64(res[0], "minPwdAge", 0);
439 return status;
444 samr_ChangePasswordUser2
446 easy - just a subset of samr_ChangePasswordUser3
448 NTSTATUS samr_ChangePasswordUser2(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
449 struct samr_ChangePasswordUser2 *r)
451 struct samr_ChangePasswordUser3 r2;
453 r2.in.server = r->in.server;
454 r2.in.account = r->in.account;
455 r2.in.nt_password = r->in.nt_password;
456 r2.in.nt_verifier = r->in.nt_verifier;
457 r2.in.lm_change = r->in.lm_change;
458 r2.in.lm_password = r->in.lm_password;
459 r2.in.lm_verifier = r->in.lm_verifier;
460 r2.in.password3 = NULL;
462 return samr_ChangePasswordUser3(dce_call, mem_ctx, &r2);
467 check that a password is sufficiently complex
469 static BOOL samdb_password_complexity_ok(const char *pass)
471 return check_password_quality(pass);
475 set the user password using plaintext, obeying any user or domain
476 password restrictions
478 note that this function doesn't actually store the result in the
479 database, it just fills in the "mod" structure with ldb modify
480 elements to setup the correct change when samdb_replace() is
481 called. This allows the caller to combine the change with other
482 changes (as is needed by some of the set user info levels)
484 NTSTATUS samdb_set_password(void *ctx, TALLOC_CTX *mem_ctx,
485 const char *user_dn, const char *domain_dn,
486 struct ldb_message *mod,
487 const char *new_pass,
488 struct samr_Password *lmNewHash,
489 struct samr_Password *ntNewHash,
490 BOOL user_change,
491 BOOL restrictions,
492 uint32_t *reject_reason)
494 const char * const user_attrs[] = { "userAccountControl", "lmPwdHistory",
495 "ntPwdHistory", "unicodePwd",
496 "lmPwdHash", "ntPwdHash", "badPwdCount",
497 NULL };
498 const char * const domain_attrs[] = { "pwdProperties", "pwdHistoryLength",
499 "maxPwdAge", "minPwdAge",
500 "minPwdLength", "pwdLastSet", NULL };
501 const char *unicodePwd;
502 NTTIME pwdLastSet;
503 int64_t minPwdAge;
504 uint_t minPwdLength, pwdProperties, pwdHistoryLength;
505 uint_t userAccountControl, badPwdCount;
506 struct samr_Password *lmPwdHistory, *ntPwdHistory, lmPwdHash, ntPwdHash;
507 struct samr_Password *new_lmPwdHistory, *new_ntPwdHistory;
508 struct samr_Password local_lmNewHash, local_ntNewHash;
509 int lmPwdHistory_len, ntPwdHistory_len;
510 struct ldb_message **res;
511 int count;
512 time_t now = time(NULL);
513 NTTIME now_nt;
514 int i;
516 /* we need to know the time to compute password age */
517 unix_to_nt_time(&now_nt, now);
519 /* pull all the user parameters */
520 count = gendb_search(ctx, mem_ctx, NULL, &res, user_attrs, "dn=%s", user_dn);
521 if (count != 1) {
522 return NT_STATUS_INTERNAL_DB_CORRUPTION;
524 unicodePwd = samdb_result_string(res[0], "unicodePwd", NULL);
525 userAccountControl = samdb_result_uint(res[0], "userAccountControl", 0);
526 badPwdCount = samdb_result_uint(res[0], "badPwdCount", 0);
527 lmPwdHistory_len = samdb_result_hashes(mem_ctx, res[0],
528 "lmPwdHistory", &lmPwdHistory);
529 ntPwdHistory_len = samdb_result_hashes(mem_ctx, res[0],
530 "ntPwdHistory", &ntPwdHistory);
531 lmPwdHash = samdb_result_hash(res[0], "lmPwdHash");
532 ntPwdHash = samdb_result_hash(res[0], "ntPwdHash");
533 pwdLastSet = samdb_result_uint64(res[0], "pwdLastSet", 0);
535 /* pull the domain parameters */
536 count = gendb_search(ctx, mem_ctx, NULL, &res, domain_attrs, "dn=%s", domain_dn);
537 if (count != 1) {
538 return NT_STATUS_INTERNAL_DB_CORRUPTION;
540 pwdProperties = samdb_result_uint(res[0], "pwdProperties", 0);
541 pwdHistoryLength = samdb_result_uint(res[0], "pwdHistoryLength", 0);
542 minPwdLength = samdb_result_uint(res[0], "minPwdLength", 0);
543 minPwdAge = samdb_result_int64(res[0], "minPwdAge", 0);
545 if (new_pass) {
546 /* check the various password restrictions */
547 if (restrictions && minPwdLength > strlen_m(new_pass)) {
548 if (reject_reason) {
549 *reject_reason = SAMR_REJECT_TOO_SHORT;
551 return NT_STATUS_PASSWORD_RESTRICTION;
554 /* possibly check password complexity */
555 if (restrictions && pwdProperties & DOMAIN_PASSWORD_COMPLEX &&
556 !samdb_password_complexity_ok(new_pass)) {
557 if (reject_reason) {
558 *reject_reason = SAMR_REJECT_COMPLEXITY;
560 return NT_STATUS_PASSWORD_RESTRICTION;
563 /* compute the new nt and lm hashes */
564 if (E_deshash(new_pass, local_lmNewHash.hash)) {
565 lmNewHash = &local_lmNewHash;
567 E_md4hash(new_pass, local_ntNewHash.hash);
568 ntNewHash = &local_ntNewHash;
571 if (restrictions && user_change) {
572 /* are all password changes disallowed? */
573 if (pwdProperties & DOMAIN_REFUSE_PASSWORD_CHANGE) {
574 if (reject_reason) {
575 *reject_reason = SAMR_REJECT_OTHER;
577 return NT_STATUS_PASSWORD_RESTRICTION;
580 /* can this user change password? */
581 if (userAccountControl & UF_PASSWD_CANT_CHANGE) {
582 if (reject_reason) {
583 *reject_reason = SAMR_REJECT_OTHER;
585 return NT_STATUS_PASSWORD_RESTRICTION;
588 /* yes, this is a minus. The ages are in negative 100nsec units! */
589 if (pwdLastSet - minPwdAge > now_nt) {
590 if (reject_reason) {
591 *reject_reason = SAMR_REJECT_OTHER;
593 return NT_STATUS_PASSWORD_RESTRICTION;
596 /* check the immediately past password */
597 if (pwdHistoryLength > 0) {
598 if (lmNewHash && memcmp(lmNewHash->hash, lmPwdHash.hash, 16) == 0) {
599 if (reject_reason) {
600 *reject_reason = SAMR_REJECT_COMPLEXITY;
602 return NT_STATUS_PASSWORD_RESTRICTION;
604 if (ntNewHash && memcmp(ntNewHash->hash, ntPwdHash.hash, 16) == 0) {
605 if (reject_reason) {
606 *reject_reason = SAMR_REJECT_COMPLEXITY;
608 return NT_STATUS_PASSWORD_RESTRICTION;
612 /* check the password history */
613 lmPwdHistory_len = MIN(lmPwdHistory_len, pwdHistoryLength);
614 ntPwdHistory_len = MIN(ntPwdHistory_len, pwdHistoryLength);
616 if (pwdHistoryLength > 0) {
617 if (unicodePwd && new_pass && strcmp(unicodePwd, new_pass) == 0) {
618 if (reject_reason) {
619 *reject_reason = SAMR_REJECT_COMPLEXITY;
621 return NT_STATUS_PASSWORD_RESTRICTION;
623 if (lmNewHash && memcmp(lmNewHash->hash, lmPwdHash.hash, 16) == 0) {
624 if (reject_reason) {
625 *reject_reason = SAMR_REJECT_COMPLEXITY;
627 return NT_STATUS_PASSWORD_RESTRICTION;
629 if (ntNewHash && memcmp(ntNewHash->hash, ntPwdHash.hash, 16) == 0) {
630 if (reject_reason) {
631 *reject_reason = SAMR_REJECT_COMPLEXITY;
633 return NT_STATUS_PASSWORD_RESTRICTION;
637 for (i=0; lmNewHash && i<lmPwdHistory_len;i++) {
638 if (memcmp(lmNewHash->hash, lmPwdHistory[i].hash, 16) == 0) {
639 if (reject_reason) {
640 *reject_reason = SAMR_REJECT_COMPLEXITY;
642 return NT_STATUS_PASSWORD_RESTRICTION;
645 for (i=0; ntNewHash && i<ntPwdHistory_len;i++) {
646 if (memcmp(ntNewHash->hash, ntPwdHistory[i].hash, 16) == 0) {
647 if (reject_reason) {
648 *reject_reason = SAMR_REJECT_COMPLEXITY;
650 return NT_STATUS_PASSWORD_RESTRICTION;
655 #define CHECK_RET(x) do { if (x != 0) return NT_STATUS_NO_MEMORY; } while(0)
657 /* the password is acceptable. Start forming the new fields */
658 if (lmNewHash) {
659 CHECK_RET(samdb_msg_add_hash(ctx, mem_ctx, mod, "lmPwdHash", lmNewHash));
660 } else {
661 CHECK_RET(samdb_msg_add_delete(ctx, mem_ctx, mod, "lmPwdHash"));
664 if (ntNewHash) {
665 CHECK_RET(samdb_msg_add_hash(ctx, mem_ctx, mod, "ntPwdHash", ntNewHash));
666 } else {
667 CHECK_RET(samdb_msg_add_delete(ctx, mem_ctx, mod, "ntPwdHash"));
670 if (new_pass && (pwdProperties & DOMAIN_PASSWORD_STORE_CLEARTEXT) &&
671 (userAccountControl & UF_ENCRYPTED_TEXT_PASSWORD_ALLOWED)) {
672 CHECK_RET(samdb_msg_add_string(ctx, mem_ctx, mod,
673 "unicodePwd", new_pass));
674 } else {
675 CHECK_RET(samdb_msg_add_delete(ctx, mem_ctx, mod, "unicodePwd"));
678 CHECK_RET(samdb_msg_add_uint64(ctx, mem_ctx, mod, "pwdLastSet", now_nt));
680 if (pwdHistoryLength == 0) {
681 CHECK_RET(samdb_msg_add_delete(ctx, mem_ctx, mod, "lmPwdHistory"));
682 CHECK_RET(samdb_msg_add_delete(ctx, mem_ctx, mod, "ntPwdHistory"));
683 return NT_STATUS_OK;
686 /* store the password history */
687 new_lmPwdHistory = talloc_array(mem_ctx, struct samr_Password,
688 pwdHistoryLength);
689 if (!new_lmPwdHistory) {
690 return NT_STATUS_NO_MEMORY;
692 new_ntPwdHistory = talloc_array(mem_ctx, struct samr_Password,
693 pwdHistoryLength);
694 if (!new_ntPwdHistory) {
695 return NT_STATUS_NO_MEMORY;
697 for (i=0;i<MIN(pwdHistoryLength-1, lmPwdHistory_len);i++) {
698 new_lmPwdHistory[i+1] = lmPwdHistory[i];
700 for (i=0;i<MIN(pwdHistoryLength-1, ntPwdHistory_len);i++) {
701 new_ntPwdHistory[i+1] = ntPwdHistory[i];
704 /* Don't store 'long' passwords in the LM history,
705 but make sure to 'expire' one password off the other end */
706 if (lmNewHash) {
707 new_lmPwdHistory[0] = *lmNewHash;
708 } else {
709 ZERO_STRUCT(new_lmPwdHistory[0]);
711 lmPwdHistory_len = MIN(lmPwdHistory_len + 1, pwdHistoryLength);
713 if (ntNewHash) {
714 new_ntPwdHistory[0] = *ntNewHash;
715 } else {
716 ZERO_STRUCT(new_ntPwdHistory[0]);
718 ntPwdHistory_len = MIN(ntPwdHistory_len + 1, pwdHistoryLength);
720 CHECK_RET(samdb_msg_add_hashes(ctx, mem_ctx, mod,
721 "lmPwdHistory",
722 new_lmPwdHistory,
723 lmPwdHistory_len));
725 CHECK_RET(samdb_msg_add_hashes(ctx, mem_ctx, mod,
726 "ntPwdHistory",
727 new_ntPwdHistory,
728 ntPwdHistory_len));
729 return NT_STATUS_OK;
733 set password via a samr_CryptPassword buffer
734 this will in the 'msg' with modify operations that will update the user
735 password when applied
737 NTSTATUS samr_set_password(struct dcesrv_call_state *dce_call,
738 void *sam_ctx,
739 const char *account_dn, const char *domain_dn,
740 TALLOC_CTX *mem_ctx,
741 struct ldb_message *msg,
742 struct samr_CryptPassword *pwbuf)
744 NTSTATUS nt_status;
745 char new_pass[512];
746 uint32_t new_pass_len;
747 DATA_BLOB session_key = data_blob(NULL, 0);
749 nt_status = dcesrv_fetch_session_key(dce_call->conn, &session_key);
750 if (!NT_STATUS_IS_OK(nt_status)) {
751 return nt_status;
754 arcfour_crypt_blob(pwbuf->data, 516, &session_key);
756 if (!decode_pw_buffer(pwbuf->data, new_pass, sizeof(new_pass),
757 &new_pass_len, STR_UNICODE)) {
758 DEBUG(3,("samr: failed to decode password buffer\n"));
759 return NT_STATUS_WRONG_PASSWORD;
762 /* set the password - samdb needs to know both the domain and user DNs,
763 so the domain password policy can be used */
764 return samdb_set_password(sam_ctx, mem_ctx,
765 account_dn, domain_dn,
766 msg, new_pass,
767 NULL, NULL,
768 False, /* This is a password set, not change */
769 True, /* run restriction tests */
770 NULL);
775 set password via a samr_CryptPasswordEx buffer
776 this will in the 'msg' with modify operations that will update the user
777 password when applied
779 NTSTATUS samr_set_password_ex(struct dcesrv_call_state *dce_call,
780 void *sam_ctx,
781 const char *account_dn, const char *domain_dn,
782 TALLOC_CTX *mem_ctx,
783 struct ldb_message *msg,
784 struct samr_CryptPasswordEx *pwbuf)
786 NTSTATUS nt_status;
787 char new_pass[512];
788 uint32_t new_pass_len;
789 DATA_BLOB co_session_key;
790 DATA_BLOB session_key = data_blob(NULL, 0);
791 struct MD5Context ctx;
793 nt_status = dcesrv_fetch_session_key(dce_call->conn, &session_key);
794 if (!NT_STATUS_IS_OK(nt_status)) {
795 return nt_status;
798 co_session_key = data_blob_talloc(mem_ctx, NULL, 16);
799 if (!co_session_key.data) {
800 return NT_STATUS_NO_MEMORY;
803 MD5Init(&ctx);
804 MD5Update(&ctx, &pwbuf->data[516], 16);
805 MD5Update(&ctx, session_key.data, session_key.length);
806 MD5Final(co_session_key.data, &ctx);
808 arcfour_crypt_blob(pwbuf->data, 516, &co_session_key);
810 if (!decode_pw_buffer(pwbuf->data, new_pass, sizeof(new_pass),
811 &new_pass_len, STR_UNICODE)) {
812 DEBUG(3,("samr: failed to decode password buffer\n"));
813 return NT_STATUS_WRONG_PASSWORD;
816 /* set the password - samdb needs to know both the domain and user DNs,
817 so the domain password policy can be used */
818 return samdb_set_password(sam_ctx, mem_ctx,
819 account_dn, domain_dn,
820 msg, new_pass,
821 NULL, NULL,
822 False, /* This is a password set, not change */
823 True, /* run restriction tests */
824 NULL);