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 3 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, see <http://www.gnu.org/licenses/>.
24 #include "rpc_server/dcerpc_server.h"
25 #include "rpc_server/common/common.h"
26 #include "rpc_server/samr/dcesrv_samr.h"
27 #include "system/time.h"
28 #include "../lib/crypto/crypto.h"
29 #include "dsdb/common/flags.h"
30 #include "libcli/ldap/ldap.h"
31 #include "dsdb/samdb/samdb.h"
32 #include "auth/auth.h"
33 #include "rpc_server/samr/proto.h"
34 #include "libcli/auth/libcli_auth.h"
35 #include "../lib/util/util_ldb.h"
36 #include "param/param.h"
39 samr_ChangePasswordUser
41 NTSTATUS dcesrv_samr_ChangePasswordUser(struct dcesrv_call_state *dce_call,
43 struct samr_ChangePasswordUser *r)
45 struct dcesrv_handle *h;
46 struct samr_account_state *a_state;
47 struct ldb_context *sam_ctx;
48 struct ldb_message **res, *msg;
50 struct samr_Password new_lmPwdHash, new_ntPwdHash, checkHash;
51 struct samr_Password *lm_pwd, *nt_pwd;
52 NTSTATUS status = NT_STATUS_OK;
53 const char * const attrs[] = { "dBCSPwd", "unicodePwd" , NULL };
55 DCESRV_PULL_HANDLE(h, r->in.user_handle, SAMR_HANDLE_USER);
59 /* basic sanity checking on parameters. Do this before any database ops */
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
65 return NT_STATUS_INVALID_PARAMETER_MIX;
68 /* To change a password we need to open as system */
69 sam_ctx = samdb_connect(mem_ctx, dce_call->event_ctx, dce_call->conn->dce_ctx->lp_ctx, system_session(mem_ctx, dce_call->conn->dce_ctx->lp_ctx));
70 if (sam_ctx == NULL) {
71 return NT_STATUS_INVALID_SYSTEM_SERVICE;
74 ret = ldb_transaction_start(sam_ctx);
76 DEBUG(1, ("Failed to start transaction: %s\n", ldb_errstring(sam_ctx)));
77 return NT_STATUS_TRANSACTION_ABORTED;
80 /* fetch the old hashes */
81 ret = gendb_search_dn(sam_ctx, mem_ctx,
82 a_state->account_dn, &res, attrs);
84 ldb_transaction_cancel(sam_ctx);
85 return NT_STATUS_WRONG_PASSWORD;
89 status = samdb_result_passwords(mem_ctx, dce_call->conn->dce_ctx->lp_ctx,
90 msg, &lm_pwd, &nt_pwd);
91 if (!NT_STATUS_IS_OK(status) || !lm_pwd || !nt_pwd) {
92 ldb_transaction_cancel(sam_ctx);
93 return NT_STATUS_WRONG_PASSWORD;
96 /* decrypt and check the new lm hash */
97 D_P16(lm_pwd->hash, r->in.new_lm_crypted->hash, new_lmPwdHash.hash);
98 D_P16(new_lmPwdHash.hash, r->in.old_lm_crypted->hash, checkHash.hash);
99 if (memcmp(checkHash.hash, lm_pwd, 16) != 0) {
100 ldb_transaction_cancel(sam_ctx);
101 return NT_STATUS_WRONG_PASSWORD;
104 /* decrypt and check the new nt hash */
105 D_P16(nt_pwd->hash, r->in.new_nt_crypted->hash, new_ntPwdHash.hash);
106 D_P16(new_ntPwdHash.hash, r->in.old_nt_crypted->hash, checkHash.hash);
107 if (memcmp(checkHash.hash, nt_pwd, 16) != 0) {
108 ldb_transaction_cancel(sam_ctx);
109 return NT_STATUS_WRONG_PASSWORD;
112 /* The NT Cross is not required by Win2k3 R2, but if present
113 check the nt cross hash */
114 if (r->in.cross1_present && r->in.nt_cross) {
115 D_P16(lm_pwd->hash, r->in.nt_cross->hash, checkHash.hash);
116 if (memcmp(checkHash.hash, new_ntPwdHash.hash, 16) != 0) {
117 ldb_transaction_cancel(sam_ctx);
118 return NT_STATUS_WRONG_PASSWORD;
122 /* The LM Cross is not required by Win2k3 R2, but if present
123 check the lm cross hash */
124 if (r->in.cross2_present && r->in.lm_cross) {
125 D_P16(nt_pwd->hash, r->in.lm_cross->hash, checkHash.hash);
126 if (memcmp(checkHash.hash, new_lmPwdHash.hash, 16) != 0) {
127 ldb_transaction_cancel(sam_ctx);
128 return NT_STATUS_WRONG_PASSWORD;
132 msg = ldb_msg_new(mem_ctx);
134 ldb_transaction_cancel(sam_ctx);
135 return NT_STATUS_NO_MEMORY;
138 msg->dn = ldb_dn_copy(msg, a_state->account_dn);
140 ldb_transaction_cancel(sam_ctx);
141 return NT_STATUS_NO_MEMORY;
144 /* setup password modify mods on the user DN specified. This may fail
145 * due to password policies. */
146 status = samdb_set_password(sam_ctx, mem_ctx,
147 a_state->account_dn, a_state->domain_state->domain_dn,
148 msg, NULL, &new_lmPwdHash, &new_ntPwdHash,
149 true, /* this is a user password change */
152 if (!NT_STATUS_IS_OK(status)) {
153 ldb_transaction_cancel(sam_ctx);
157 /* The above call only setup the modifications, this actually
158 * makes the write to the database. */
159 ret = samdb_replace(sam_ctx, mem_ctx, msg);
161 DEBUG(2,("Failed to modify record to change password on %s: %s\n",
162 ldb_dn_get_linearized(a_state->account_dn),
163 ldb_errstring(sam_ctx)));
164 ldb_transaction_cancel(sam_ctx);
165 return NT_STATUS_INTERNAL_DB_CORRUPTION;
168 /* And this confirms it in a transaction commit */
169 ret = ldb_transaction_commit(sam_ctx);
171 DEBUG(1,("Failed to commit transaction to change password on %s: %s\n",
172 ldb_dn_get_linearized(a_state->account_dn),
173 ldb_errstring(sam_ctx)));
174 return NT_STATUS_TRANSACTION_ABORTED;
181 samr_OemChangePasswordUser2
183 NTSTATUS dcesrv_samr_OemChangePasswordUser2(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
184 struct samr_OemChangePasswordUser2 *r)
187 DATA_BLOB new_password, new_unicode_password;
189 struct samr_CryptPassword *pwbuf = r->in.password;
190 struct ldb_context *sam_ctx;
191 struct ldb_dn *user_dn;
193 struct ldb_message **res, *mod;
194 const char * const attrs[] = { "objectSid", "dBCSPwd", NULL };
195 struct samr_Password *lm_pwd;
196 DATA_BLOB lm_pwd_blob;
197 uint8_t new_lm_hash[16];
198 struct samr_Password lm_verifier;
199 ssize_t unicode_pw_len;
202 return NT_STATUS_INVALID_PARAMETER;
205 if (r->in.hash == NULL) {
206 return NT_STATUS_INVALID_PARAMETER;
209 /* To change a password we need to open as system */
210 sam_ctx = samdb_connect(mem_ctx, dce_call->event_ctx, dce_call->conn->dce_ctx->lp_ctx, system_session(mem_ctx, dce_call->conn->dce_ctx->lp_ctx));
211 if (sam_ctx == NULL) {
212 return NT_STATUS_INVALID_SYSTEM_SERVICE;
215 ret = ldb_transaction_start(sam_ctx);
217 DEBUG(1, ("Failed to start transaction: %s\n", ldb_errstring(sam_ctx)));
218 return NT_STATUS_TRANSACTION_ABORTED;
221 /* we need the users dn and the domain dn (derived from the
222 user SID). We also need the current lm password hash in
223 order to decrypt the incoming password */
224 ret = gendb_search(sam_ctx,
225 mem_ctx, NULL, &res, attrs,
226 "(&(sAMAccountName=%s)(objectclass=user))",
227 r->in.account->string);
229 ldb_transaction_cancel(sam_ctx);
230 /* Don't give the game away: (don't allow anonymous users to prove the existance of usernames) */
231 return NT_STATUS_WRONG_PASSWORD;
234 user_dn = res[0]->dn;
236 status = samdb_result_passwords(mem_ctx, dce_call->conn->dce_ctx->lp_ctx,
237 res[0], &lm_pwd, NULL);
238 if (!NT_STATUS_IS_OK(status) || !lm_pwd) {
239 ldb_transaction_cancel(sam_ctx);
240 return NT_STATUS_WRONG_PASSWORD;
243 /* decrypt the password we have been given */
244 lm_pwd_blob = data_blob(lm_pwd->hash, sizeof(lm_pwd->hash));
245 arcfour_crypt_blob(pwbuf->data, 516, &lm_pwd_blob);
246 data_blob_free(&lm_pwd_blob);
248 if (!extract_pw_from_buffer(mem_ctx, pwbuf->data, &new_password)) {
249 ldb_transaction_cancel(sam_ctx);
250 DEBUG(3,("samr: failed to decode password buffer\n"));
251 return NT_STATUS_WRONG_PASSWORD;
254 if (convert_string_talloc_convenience(mem_ctx, lp_iconv_convenience(dce_call->conn->dce_ctx->lp_ctx),
256 (const char *)new_password.data,
258 (void **)&new_pass) == -1) {
259 DEBUG(3,("samr: failed to convert incoming password buffer to unix charset\n"));
260 ldb_transaction_cancel(sam_ctx);
261 return NT_STATUS_WRONG_PASSWORD;
264 unicode_pw_len = convert_string_talloc_convenience(mem_ctx, lp_iconv_convenience(dce_call->conn->dce_ctx->lp_ctx),
266 (const char *)new_password.data,
268 (void **)&new_unicode_password.data);
269 if (unicode_pw_len == -1) {
270 DEBUG(3,("samr: failed to convert incoming password buffer to UTF16 charset\n"));
271 ldb_transaction_cancel(sam_ctx);
272 return NT_STATUS_WRONG_PASSWORD;
274 new_unicode_password.length = unicode_pw_len;
276 E_deshash(new_pass, new_lm_hash);
277 E_old_pw_hash(new_lm_hash, lm_pwd->hash, lm_verifier.hash);
278 if (memcmp(lm_verifier.hash, r->in.hash->hash, 16) != 0) {
279 ldb_transaction_cancel(sam_ctx);
280 return NT_STATUS_WRONG_PASSWORD;
283 mod = ldb_msg_new(mem_ctx);
285 ldb_transaction_cancel(sam_ctx);
286 return NT_STATUS_NO_MEMORY;
289 mod->dn = ldb_dn_copy(mod, user_dn);
291 ldb_transaction_cancel(sam_ctx);
292 return NT_STATUS_NO_MEMORY;
295 /* set the password on the user DN specified. This may fail
296 * due to password policies */
297 status = samdb_set_password(sam_ctx, mem_ctx,
299 mod, &new_unicode_password,
301 true, /* this is a user password change */
304 if (!NT_STATUS_IS_OK(status)) {
305 ldb_transaction_cancel(sam_ctx);
309 /* The above call only setup the modifications, this actually
310 * makes the write to the database. */
311 ret = samdb_replace(sam_ctx, mem_ctx, mod);
313 DEBUG(2,("Failed to modify record to change password on %s: %s\n",
314 ldb_dn_get_linearized(user_dn),
315 ldb_errstring(sam_ctx)));
316 ldb_transaction_cancel(sam_ctx);
317 return NT_STATUS_INTERNAL_DB_CORRUPTION;
320 /* And this confirms it in a transaction commit */
321 ret = ldb_transaction_commit(sam_ctx);
323 DEBUG(1,("Failed to commit transaction to change password on %s: %s\n",
324 ldb_dn_get_linearized(user_dn),
325 ldb_errstring(sam_ctx)));
326 return NT_STATUS_TRANSACTION_ABORTED;
334 samr_ChangePasswordUser3
336 NTSTATUS dcesrv_samr_ChangePasswordUser3(struct dcesrv_call_state *dce_call,
338 struct samr_ChangePasswordUser3 *r)
341 DATA_BLOB new_password;
342 struct ldb_context *sam_ctx = NULL;
343 struct ldb_dn *user_dn;
345 struct ldb_message **res, *mod;
346 const char * const attrs[] = { "unicodePwd", "dBCSPwd", NULL };
347 struct samr_Password *nt_pwd, *lm_pwd;
348 DATA_BLOB nt_pwd_blob;
349 struct samr_DomInfo1 *dominfo = NULL;
350 struct samr_ChangeReject *reject = NULL;
351 enum samr_RejectReason reason = SAMR_REJECT_OTHER;
352 uint8_t new_nt_hash[16], new_lm_hash[16];
353 struct samr_Password nt_verifier, lm_verifier;
355 *r->out.dominfo = NULL;
356 *r->out.reject = NULL;
358 if (r->in.nt_password == NULL ||
359 r->in.nt_verifier == NULL) {
360 return NT_STATUS_INVALID_PARAMETER;
363 /* To change a password we need to open as system */
364 sam_ctx = samdb_connect(mem_ctx, dce_call->event_ctx, dce_call->conn->dce_ctx->lp_ctx, system_session(mem_ctx, dce_call->conn->dce_ctx->lp_ctx));
365 if (sam_ctx == NULL) {
366 return NT_STATUS_INVALID_SYSTEM_SERVICE;
369 ret = ldb_transaction_start(sam_ctx);
371 talloc_free(sam_ctx);
372 DEBUG(1, ("Failed to start transaction: %s\n", ldb_errstring(sam_ctx)));
373 return NT_STATUS_TRANSACTION_ABORTED;
376 /* we need the users dn and the domain dn (derived from the
377 user SID). We also need the current lm and nt password hashes
378 in order to decrypt the incoming passwords */
379 ret = gendb_search(sam_ctx,
380 mem_ctx, NULL, &res, attrs,
381 "(&(sAMAccountName=%s)(objectclass=user))",
382 r->in.account->string);
384 /* Don't give the game away: (don't allow anonymous users to prove the existance of usernames) */
385 status = NT_STATUS_WRONG_PASSWORD;
389 user_dn = res[0]->dn;
391 status = samdb_result_passwords(mem_ctx, dce_call->conn->dce_ctx->lp_ctx,
392 res[0], &lm_pwd, &nt_pwd);
393 if (!NT_STATUS_IS_OK(status) ) {
398 status = NT_STATUS_WRONG_PASSWORD;
402 /* decrypt the password we have been given */
403 nt_pwd_blob = data_blob(nt_pwd->hash, sizeof(nt_pwd->hash));
404 arcfour_crypt_blob(r->in.nt_password->data, 516, &nt_pwd_blob);
405 data_blob_free(&nt_pwd_blob);
407 if (!extract_pw_from_buffer(mem_ctx, r->in.nt_password->data, &new_password)) {
408 ldb_transaction_cancel(sam_ctx);
409 DEBUG(3,("samr: failed to decode password buffer\n"));
410 return NT_STATUS_WRONG_PASSWORD;
413 if (r->in.nt_verifier == NULL) {
414 status = NT_STATUS_WRONG_PASSWORD;
418 /* check NT verifier */
419 mdfour(new_nt_hash, new_password.data, new_password.length);
421 E_old_pw_hash(new_nt_hash, nt_pwd->hash, nt_verifier.hash);
422 if (memcmp(nt_verifier.hash, r->in.nt_verifier->hash, 16) != 0) {
423 status = NT_STATUS_WRONG_PASSWORD;
427 /* check LM verifier (really not needed as we just checked the
428 * much stronger NT hash, but the RPC-SAMR test checks for
430 if (lm_pwd && r->in.lm_verifier != NULL) {
432 if (convert_string_talloc_convenience(mem_ctx, lp_iconv_convenience(dce_call->conn->dce_ctx->lp_ctx),
434 (const char *)new_password.data,
436 (void **)&new_pass) != -1) {
437 E_deshash(new_pass, new_lm_hash);
438 E_old_pw_hash(new_nt_hash, lm_pwd->hash, lm_verifier.hash);
439 if (memcmp(lm_verifier.hash, r->in.lm_verifier->hash, 16) != 0) {
440 status = NT_STATUS_WRONG_PASSWORD;
446 mod = ldb_msg_new(mem_ctx);
448 status = NT_STATUS_NO_MEMORY;
452 mod->dn = ldb_dn_copy(mod, user_dn);
454 status = NT_STATUS_NO_MEMORY;
458 /* set the password on the user DN specified. This may fail
459 * due to password policies */
460 status = samdb_set_password(sam_ctx, mem_ctx,
464 true, /* this is a user password change */
467 if (!NT_STATUS_IS_OK(status)) {
471 /* The above call only setup the modifications, this actually
472 * makes the write to the database. */
473 ret = samdb_replace(sam_ctx, mem_ctx, mod);
475 DEBUG(2,("samdb_replace failed to change password for %s: %s\n",
476 ldb_dn_get_linearized(user_dn),
477 ldb_errstring(sam_ctx)));
478 status = NT_STATUS_UNSUCCESSFUL;
482 /* And this confirms it in a transaction commit */
483 ret = ldb_transaction_commit(sam_ctx);
485 DEBUG(1,("Failed to commit transaction to change password on %s: %s\n",
486 ldb_dn_get_linearized(user_dn),
487 ldb_errstring(sam_ctx)));
488 status = NT_STATUS_TRANSACTION_ABORTED;
495 ldb_transaction_cancel(sam_ctx);
496 talloc_free(sam_ctx);
498 reject = talloc(mem_ctx, struct samr_ChangeReject);
499 *r->out.dominfo = dominfo;
500 *r->out.reject = reject;
502 if (reject == NULL) {
505 ZERO_STRUCTP(reject);
507 reject->reason = reason;
514 samr_ChangePasswordUser2
516 easy - just a subset of samr_ChangePasswordUser3
518 NTSTATUS dcesrv_samr_ChangePasswordUser2(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
519 struct samr_ChangePasswordUser2 *r)
521 struct samr_ChangePasswordUser3 r2;
522 struct samr_DomInfo1 *dominfo = NULL;
523 struct samr_ChangeReject *reject = NULL;
525 r2.in.server = r->in.server;
526 r2.in.account = r->in.account;
527 r2.in.nt_password = r->in.nt_password;
528 r2.in.nt_verifier = r->in.nt_verifier;
529 r2.in.lm_change = r->in.lm_change;
530 r2.in.lm_password = r->in.lm_password;
531 r2.in.lm_verifier = r->in.lm_verifier;
532 r2.in.password3 = NULL;
533 r2.out.dominfo = &dominfo;
534 r2.out.reject = &reject;
536 return dcesrv_samr_ChangePasswordUser3(dce_call, mem_ctx, &r2);
541 set password via a samr_CryptPassword buffer
542 this will in the 'msg' with modify operations that will update the user
543 password when applied
545 NTSTATUS samr_set_password(struct dcesrv_call_state *dce_call,
547 struct ldb_dn *account_dn, struct ldb_dn *domain_dn,
549 struct ldb_message *msg,
550 struct samr_CryptPassword *pwbuf)
553 DATA_BLOB new_password;
554 DATA_BLOB session_key = data_blob(NULL, 0);
556 nt_status = dcesrv_fetch_session_key(dce_call->conn, &session_key);
557 if (!NT_STATUS_IS_OK(nt_status)) {
561 arcfour_crypt_blob(pwbuf->data, 516, &session_key);
563 if (!extract_pw_from_buffer(mem_ctx, pwbuf->data, &new_password)) {
564 DEBUG(3,("samr: failed to decode password buffer\n"));
565 return NT_STATUS_WRONG_PASSWORD;
568 /* set the password - samdb needs to know both the domain and user DNs,
569 so the domain password policy can be used */
570 return samdb_set_password(sam_ctx, mem_ctx,
571 account_dn, domain_dn,
574 false, /* This is a password set, not change */
580 set password via a samr_CryptPasswordEx buffer
581 this will in the 'msg' with modify operations that will update the user
582 password when applied
584 NTSTATUS samr_set_password_ex(struct dcesrv_call_state *dce_call,
585 struct ldb_context *sam_ctx,
586 struct ldb_dn *account_dn, struct ldb_dn *domain_dn,
588 struct ldb_message *msg,
589 struct samr_CryptPasswordEx *pwbuf)
592 DATA_BLOB new_password;
593 DATA_BLOB co_session_key;
594 DATA_BLOB session_key = data_blob(NULL, 0);
595 struct MD5Context ctx;
597 nt_status = dcesrv_fetch_session_key(dce_call->conn, &session_key);
598 if (!NT_STATUS_IS_OK(nt_status)) {
602 co_session_key = data_blob_talloc(mem_ctx, NULL, 16);
603 if (!co_session_key.data) {
604 return NT_STATUS_NO_MEMORY;
608 MD5Update(&ctx, &pwbuf->data[516], 16);
609 MD5Update(&ctx, session_key.data, session_key.length);
610 MD5Final(co_session_key.data, &ctx);
612 arcfour_crypt_blob(pwbuf->data, 516, &co_session_key);
614 if (!extract_pw_from_buffer(mem_ctx, pwbuf->data, &new_password)) {
615 DEBUG(3,("samr: failed to decode password buffer\n"));
616 return NT_STATUS_WRONG_PASSWORD;
619 /* set the password - samdb needs to know both the domain and user DNs,
620 so the domain password policy can be used */
621 return samdb_set_password(sam_ctx, mem_ctx,
622 account_dn, domain_dn,
625 false, /* This is a password set, not change */