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/samr/dcesrv_samr.h"
26 #include "system/time.h"
27 #include "../lib/crypto/crypto.h"
28 #include "dsdb/samdb/samdb.h"
29 #include "auth/auth.h"
30 #include "libcli/auth/libcli_auth.h"
31 #include "../lib/util/util_ldb.h"
32 #include "rpc_server/samr/proto.h"
33 #include "auth/auth_sam.h"
35 static void log_password_change_event(struct imessaging_context *msg_ctx,
36 struct loadparm_context *lp_ctx,
37 const struct tsocket_address *remote_client_address,
38 const struct tsocket_address *local_server_address,
39 const char *auth_description,
40 const char *password_type,
41 const char *original_client_name,
42 const char *account_name_from_db,
47 * Forcing this via the NTLM auth structure is not ideal, but
48 * it is the most practical option right now, and ensures the
49 * logs are consistent, even if some elements are always NULL.
51 struct auth_usersupplied_info ui = {
55 .account_name = original_client_name,
56 .domain_name = lpcfg_sam_name(lp_ctx),
59 .account_name = account_name_from_db,
60 .domain_name = lpcfg_sam_name(lp_ctx),
62 .remote_host = remote_client_address,
63 .local_host = local_server_address,
64 .service_description = "SAMR Password Change",
65 .auth_description = auth_description,
66 .password_type = password_type,
69 log_authentication_event(msg_ctx,
74 ui.mapped.domain_name,
75 ui.mapped.account_name,
80 samr_ChangePasswordUser
82 So old it is just not worth implementing
83 because it does not supply a plaintext and so we can't do password
84 complexity checking and cannot update all the other password hashes.
87 NTSTATUS dcesrv_samr_ChangePasswordUser(struct dcesrv_call_state *dce_call,
89 struct samr_ChangePasswordUser *r)
91 return NT_STATUS_NOT_IMPLEMENTED;
95 samr_OemChangePasswordUser2
97 NTSTATUS dcesrv_samr_OemChangePasswordUser2(struct dcesrv_call_state *dce_call,
99 struct samr_OemChangePasswordUser2 *r)
101 NTSTATUS status = NT_STATUS_WRONG_PASSWORD;
102 DATA_BLOB new_password, new_unicode_password;
104 struct samr_CryptPassword *pwbuf = r->in.password;
105 struct ldb_context *sam_ctx;
106 struct ldb_dn *user_dn;
108 struct ldb_message **res;
109 const char * const attrs[] = { "objectSid", "dBCSPwd",
110 "userAccountControl",
112 "msDS-User-Account-Control-Computed",
113 "badPwdCount", "badPasswordTime",
116 struct samr_Password *lm_pwd;
117 DATA_BLOB lm_pwd_blob;
118 uint8_t new_lm_hash[16];
119 struct samr_Password lm_verifier;
120 size_t unicode_pw_len;
121 size_t converted_size = 0;
122 const char *user_samAccountName = NULL;
123 struct dom_sid *user_objectSid = NULL;
126 return NT_STATUS_INVALID_PARAMETER;
129 if (r->in.hash == NULL) {
130 return NT_STATUS_INVALID_PARAMETER;
133 /* this call can only work with lanman auth */
134 if (!lpcfg_lanman_auth(dce_call->conn->dce_ctx->lp_ctx)) {
135 return NT_STATUS_ACCESS_DISABLED_BY_POLICY_OTHER;
138 /* Connect to a SAMDB with system privileges for fetching the old pw
140 sam_ctx = samdb_connect(mem_ctx,
142 dce_call->conn->dce_ctx->lp_ctx,
143 system_session(dce_call->conn->dce_ctx->lp_ctx),
144 dce_call->conn->remote_address,
146 if (sam_ctx == NULL) {
147 return NT_STATUS_INVALID_SYSTEM_SERVICE;
150 /* we need the users dn and the domain dn (derived from the
151 user SID). We also need the current lm password hash in
152 order to decrypt the incoming password */
153 ret = gendb_search(sam_ctx,
154 mem_ctx, NULL, &res, attrs,
155 "(&(sAMAccountName=%s)(objectclass=user))",
156 ldb_binary_encode_string(mem_ctx, r->in.account->string));
158 status = NT_STATUS_NO_SUCH_USER; /* Converted to WRONG_PASSWORD below */
162 user_dn = res[0]->dn;
164 user_samAccountName = ldb_msg_find_attr_as_string(res[0], "samAccountName", NULL);
165 user_objectSid = samdb_result_dom_sid(res, res[0], "objectSid");
167 status = samdb_result_passwords(mem_ctx, dce_call->conn->dce_ctx->lp_ctx,
168 res[0], &lm_pwd, NULL);
169 if (!NT_STATUS_IS_OK(status)) {
171 } else if (!lm_pwd) {
172 status = 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 (!extract_pw_from_buffer(mem_ctx, pwbuf->data, &new_password)) {
182 DEBUG(3,("samr: failed to decode password buffer\n"));
183 authsam_update_bad_pwd_count(sam_ctx, res[0], ldb_get_default_basedn(sam_ctx));
184 status = NT_STATUS_WRONG_PASSWORD;
188 if (!convert_string_talloc_handle(mem_ctx, lpcfg_iconv_handle(dce_call->conn->dce_ctx->lp_ctx),
190 (const char *)new_password.data,
192 (void **)&new_pass, &converted_size)) {
193 DEBUG(3,("samr: failed to convert incoming password buffer to unix charset\n"));
194 authsam_update_bad_pwd_count(sam_ctx, res[0], ldb_get_default_basedn(sam_ctx));
195 status = NT_STATUS_WRONG_PASSWORD;
199 if (!convert_string_talloc_handle(mem_ctx, lpcfg_iconv_handle(dce_call->conn->dce_ctx->lp_ctx),
201 (const char *)new_password.data,
203 (void **)&new_unicode_password.data, &unicode_pw_len)) {
204 DEBUG(3,("samr: failed to convert incoming password buffer to UTF16 charset\n"));
205 authsam_update_bad_pwd_count(sam_ctx, res[0], ldb_get_default_basedn(sam_ctx));
206 status = NT_STATUS_WRONG_PASSWORD;
209 new_unicode_password.length = unicode_pw_len;
211 E_deshash(new_pass, new_lm_hash);
212 E_old_pw_hash(new_lm_hash, lm_pwd->hash, lm_verifier.hash);
213 if (memcmp(lm_verifier.hash, r->in.hash->hash, 16) != 0) {
214 authsam_update_bad_pwd_count(sam_ctx, res[0], ldb_get_default_basedn(sam_ctx));
215 status = NT_STATUS_WRONG_PASSWORD;
219 /* Connect to a SAMDB with user privileges for the password change */
220 sam_ctx = samdb_connect(mem_ctx,
222 dce_call->conn->dce_ctx->lp_ctx,
223 dce_call->conn->auth_state.session_info,
224 dce_call->conn->remote_address,
226 if (sam_ctx == NULL) {
227 return NT_STATUS_INVALID_SYSTEM_SERVICE;
230 /* Start transaction */
231 ret = ldb_transaction_start(sam_ctx);
232 if (ret != LDB_SUCCESS) {
233 DEBUG(1, ("Failed to start transaction: %s\n", ldb_errstring(sam_ctx)));
234 return NT_STATUS_TRANSACTION_ABORTED;
237 /* Performs the password modification. We pass the old hashes read out
238 * from the database since they were already checked against the user-
240 status = samdb_set_password(sam_ctx, mem_ctx,
242 &new_unicode_password,
244 lm_pwd, NULL, /* this is a user password change */
247 if (!NT_STATUS_IS_OK(status)) {
248 ldb_transaction_cancel(sam_ctx);
252 /* And this confirms it in a transaction commit */
253 ret = ldb_transaction_commit(sam_ctx);
254 if (ret != LDB_SUCCESS) {
255 DEBUG(1,("Failed to commit transaction to change password on %s: %s\n",
256 ldb_dn_get_linearized(user_dn),
257 ldb_errstring(sam_ctx)));
258 status = NT_STATUS_TRANSACTION_ABORTED;
262 status = NT_STATUS_OK;
266 log_password_change_event(dce_call->conn->msg_ctx,
267 dce_call->conn->dce_ctx->lp_ctx,
268 dce_call->conn->remote_address,
269 dce_call->conn->local_address,
270 "OemChangePasswordUser2",
271 "RC4/DES using LanMan-hash",
272 r->in.account->string,
276 if (NT_STATUS_IS_OK(status)) {
279 /* Only update the badPwdCount if we found the user */
280 if (NT_STATUS_EQUAL(status, NT_STATUS_WRONG_PASSWORD)) {
281 authsam_update_bad_pwd_count(sam_ctx, res[0], ldb_get_default_basedn(sam_ctx));
282 } else if (NT_STATUS_EQUAL(status, NT_STATUS_NO_SUCH_USER)) {
283 /* Don't give the game away: (don't allow anonymous users to prove the existence of usernames) */
284 status = NT_STATUS_WRONG_PASSWORD;
292 samr_ChangePasswordUser3
294 NTSTATUS dcesrv_samr_ChangePasswordUser3(struct dcesrv_call_state *dce_call,
296 struct samr_ChangePasswordUser3 *r)
298 NTSTATUS status = NT_STATUS_WRONG_PASSWORD;
299 DATA_BLOB new_password;
300 struct ldb_context *sam_ctx = NULL;
301 struct ldb_dn *user_dn = NULL;
303 struct ldb_message **res;
304 const char * const attrs[] = { "unicodePwd", "dBCSPwd",
305 "userAccountControl",
307 "msDS-User-Account-Control-Computed",
308 "badPwdCount", "badPasswordTime",
310 struct samr_Password *nt_pwd, *lm_pwd;
311 DATA_BLOB nt_pwd_blob;
312 struct samr_DomInfo1 *dominfo = NULL;
313 struct userPwdChangeFailureInformation *reject = NULL;
314 enum samPwdChangeReason reason = SAM_PWD_CHANGE_NO_ERROR;
315 uint8_t new_nt_hash[16], new_lm_hash[16];
316 struct samr_Password nt_verifier, lm_verifier;
317 const char *user_samAccountName = NULL;
318 struct dom_sid *user_objectSid = NULL;
319 enum ntlm_auth_level ntlm_auth_level
320 = lpcfg_ntlm_auth(dce_call->conn->dce_ctx->lp_ctx);
322 *r->out.dominfo = NULL;
323 *r->out.reject = NULL;
325 /* this call should be disabled without NTLM auth */
326 if (ntlm_auth_level == NTLM_AUTH_DISABLED) {
327 DBG_WARNING("NTLM password changes not"
328 "permitted by configuration.\n");
329 return NT_STATUS_NTLM_BLOCKED;
332 if (r->in.nt_password == NULL ||
333 r->in.nt_verifier == NULL) {
334 return NT_STATUS_INVALID_PARAMETER;
337 /* Connect to a SAMDB with system privileges for fetching the old pw
339 sam_ctx = samdb_connect(mem_ctx,
341 dce_call->conn->dce_ctx->lp_ctx,
342 system_session(dce_call->conn->dce_ctx->lp_ctx),
343 dce_call->conn->remote_address,
345 if (sam_ctx == NULL) {
346 return NT_STATUS_INVALID_SYSTEM_SERVICE;
349 /* we need the users dn and the domain dn (derived from the
350 user SID). We also need the current lm and nt password hashes
351 in order to decrypt the incoming passwords */
352 ret = gendb_search(sam_ctx,
353 mem_ctx, NULL, &res, attrs,
354 "(&(sAMAccountName=%s)(objectclass=user))",
355 ldb_binary_encode_string(mem_ctx, r->in.account->string));
357 status = NT_STATUS_NO_SUCH_USER; /* Converted to WRONG_PASSWORD below */
361 user_dn = res[0]->dn;
362 user_samAccountName = ldb_msg_find_attr_as_string(res[0], "samAccountName", NULL);
363 user_objectSid = samdb_result_dom_sid(res, res[0], "objectSid");
365 status = samdb_result_passwords(mem_ctx, dce_call->conn->dce_ctx->lp_ctx,
366 res[0], &lm_pwd, &nt_pwd);
367 if (!NT_STATUS_IS_OK(status) ) {
372 status = NT_STATUS_WRONG_PASSWORD;
376 /* decrypt the password we have been given */
377 nt_pwd_blob = data_blob(nt_pwd->hash, sizeof(nt_pwd->hash));
378 arcfour_crypt_blob(r->in.nt_password->data, 516, &nt_pwd_blob);
379 data_blob_free(&nt_pwd_blob);
381 if (!extract_pw_from_buffer(mem_ctx, r->in.nt_password->data, &new_password)) {
382 DEBUG(3,("samr: failed to decode password buffer\n"));
383 status = NT_STATUS_WRONG_PASSWORD;
387 if (r->in.nt_verifier == NULL) {
388 status = NT_STATUS_WRONG_PASSWORD;
392 /* check NT verifier */
393 mdfour(new_nt_hash, new_password.data, new_password.length);
395 E_old_pw_hash(new_nt_hash, nt_pwd->hash, nt_verifier.hash);
396 if (memcmp(nt_verifier.hash, r->in.nt_verifier->hash, 16) != 0) {
397 status = NT_STATUS_WRONG_PASSWORD;
401 /* check LM verifier (really not needed as we just checked the
402 * much stronger NT hash, but the RPC-SAMR test checks for
404 if (lm_pwd && r->in.lm_verifier != NULL) {
406 size_t converted_size = 0;
408 if (!convert_string_talloc_handle(mem_ctx, lpcfg_iconv_handle(dce_call->conn->dce_ctx->lp_ctx),
410 (const char *)new_password.data,
412 (void **)&new_pass, &converted_size)) {
413 E_deshash(new_pass, new_lm_hash);
414 E_old_pw_hash(new_nt_hash, lm_pwd->hash, lm_verifier.hash);
415 if (memcmp(lm_verifier.hash, r->in.lm_verifier->hash, 16) != 0) {
416 status = NT_STATUS_WRONG_PASSWORD;
422 /* Connect to a SAMDB with user privileges for the password change */
423 sam_ctx = samdb_connect(mem_ctx,
425 dce_call->conn->dce_ctx->lp_ctx,
426 dce_call->conn->auth_state.session_info,
427 dce_call->conn->remote_address,
429 if (sam_ctx == NULL) {
430 return NT_STATUS_INVALID_SYSTEM_SERVICE;
433 ret = ldb_transaction_start(sam_ctx);
434 if (ret != LDB_SUCCESS) {
435 DEBUG(1, ("Failed to start transaction: %s\n", ldb_errstring(sam_ctx)));
436 return NT_STATUS_TRANSACTION_ABORTED;
439 /* Performs the password modification. We pass the old hashes read out
440 * from the database since they were already checked against the user-
442 status = samdb_set_password(sam_ctx, mem_ctx,
446 lm_pwd, nt_pwd, /* this is a user password change */
450 if (!NT_STATUS_IS_OK(status)) {
451 ldb_transaction_cancel(sam_ctx);
455 /* And this confirms it in a transaction commit */
456 ret = ldb_transaction_commit(sam_ctx);
457 if (ret != LDB_SUCCESS) {
458 DEBUG(1,("Failed to commit transaction to change password on %s: %s\n",
459 ldb_dn_get_linearized(user_dn),
460 ldb_errstring(sam_ctx)));
461 status = NT_STATUS_TRANSACTION_ABORTED;
465 status = NT_STATUS_OK;
469 log_password_change_event(dce_call->conn->msg_ctx,
470 dce_call->conn->dce_ctx->lp_ctx,
471 dce_call->conn->remote_address,
472 dce_call->conn->local_address,
473 "samr_ChangePasswordUser3",
474 "RC4/DES using NTLM-hash",
475 r->in.account->string,
479 if (NT_STATUS_IS_OK(status)) {
483 /* Only update the badPwdCount if we found the user */
484 if (NT_STATUS_EQUAL(status, NT_STATUS_WRONG_PASSWORD)) {
485 authsam_update_bad_pwd_count(sam_ctx, res[0], ldb_get_default_basedn(sam_ctx));
486 } else if (NT_STATUS_EQUAL(status, NT_STATUS_NO_SUCH_USER)) {
487 /* Don't give the game away: (don't allow anonymous users to prove the existence of usernames) */
488 status = NT_STATUS_WRONG_PASSWORD;
491 reject = talloc_zero(mem_ctx, struct userPwdChangeFailureInformation);
492 if (reject != NULL) {
493 reject->extendedFailureReason = reason;
495 *r->out.reject = reject;
498 *r->out.dominfo = dominfo;
504 samr_ChangePasswordUser2
506 easy - just a subset of samr_ChangePasswordUser3
508 NTSTATUS dcesrv_samr_ChangePasswordUser2(struct dcesrv_call_state *dce_call,
510 struct samr_ChangePasswordUser2 *r)
512 struct samr_ChangePasswordUser3 r2;
513 struct samr_DomInfo1 *dominfo = NULL;
514 struct userPwdChangeFailureInformation *reject = NULL;
516 r2.in.server = r->in.server;
517 r2.in.account = r->in.account;
518 r2.in.nt_password = r->in.nt_password;
519 r2.in.nt_verifier = r->in.nt_verifier;
520 r2.in.lm_change = r->in.lm_change;
521 r2.in.lm_password = r->in.lm_password;
522 r2.in.lm_verifier = r->in.lm_verifier;
523 r2.in.password3 = NULL;
524 r2.out.dominfo = &dominfo;
525 r2.out.reject = &reject;
527 return dcesrv_samr_ChangePasswordUser3(dce_call, mem_ctx, &r2);
532 set password via a samr_CryptPassword buffer
534 NTSTATUS samr_set_password(struct dcesrv_call_state *dce_call,
535 struct ldb_context *sam_ctx,
536 struct ldb_dn *account_dn, struct ldb_dn *domain_dn,
538 struct samr_CryptPassword *pwbuf)
541 DATA_BLOB new_password;
542 DATA_BLOB session_key = data_blob(NULL, 0);
544 nt_status = dcesrv_fetch_session_key(dce_call->conn, &session_key);
545 if (!NT_STATUS_IS_OK(nt_status)) {
546 DEBUG(3,("samr: failed to get session key: %s "
547 "=> NT_STATUS_WRONG_PASSWORD\n",
548 nt_errstr(nt_status)));
549 return NT_STATUS_WRONG_PASSWORD;
552 arcfour_crypt_blob(pwbuf->data, 516, &session_key);
554 if (!extract_pw_from_buffer(mem_ctx, pwbuf->data, &new_password)) {
555 DEBUG(3,("samr: failed to decode password buffer\n"));
556 return NT_STATUS_WRONG_PASSWORD;
559 /* set the password - samdb needs to know both the domain and user DNs,
560 so the domain password policy can be used */
561 return samdb_set_password(sam_ctx, mem_ctx,
562 account_dn, domain_dn,
565 NULL, NULL, /* This is a password set, not change */
571 set password via a samr_CryptPasswordEx buffer
573 NTSTATUS samr_set_password_ex(struct dcesrv_call_state *dce_call,
574 struct ldb_context *sam_ctx,
575 struct ldb_dn *account_dn,
576 struct ldb_dn *domain_dn,
578 struct samr_CryptPasswordEx *pwbuf)
581 DATA_BLOB new_password;
582 DATA_BLOB co_session_key;
583 DATA_BLOB session_key = data_blob(NULL, 0);
586 nt_status = dcesrv_fetch_session_key(dce_call->conn, &session_key);
587 if (!NT_STATUS_IS_OK(nt_status)) {
588 DEBUG(3,("samr: failed to get session key: %s "
589 "=> NT_STATUS_WRONG_PASSWORD\n",
590 nt_errstr(nt_status)));
591 return NT_STATUS_WRONG_PASSWORD;
594 co_session_key = data_blob_talloc(mem_ctx, NULL, 16);
595 if (!co_session_key.data) {
596 return NT_STATUS_NO_MEMORY;
600 MD5Update(&ctx, &pwbuf->data[516], 16);
601 MD5Update(&ctx, session_key.data, session_key.length);
602 MD5Final(co_session_key.data, &ctx);
604 arcfour_crypt_blob(pwbuf->data, 516, &co_session_key);
606 if (!extract_pw_from_buffer(mem_ctx, pwbuf->data, &new_password)) {
607 DEBUG(3,("samr: failed to decode password buffer\n"));
608 return NT_STATUS_WRONG_PASSWORD;
611 /* set the password - samdb needs to know both the domain and user DNs,
612 so the domain password policy can be used */
613 return samdb_set_password(sam_ctx, mem_ctx,
614 account_dn, domain_dn,
617 NULL, NULL, /* This is a password set, not change */
622 set password via encrypted NT and LM hash buffers
624 NTSTATUS samr_set_password_buffers(struct dcesrv_call_state *dce_call,
625 struct ldb_context *sam_ctx,
626 struct ldb_dn *account_dn,
627 struct ldb_dn *domain_dn,
629 const uint8_t *lm_pwd_hash,
630 const uint8_t *nt_pwd_hash)
632 struct samr_Password *d_lm_pwd_hash = NULL, *d_nt_pwd_hash = NULL;
633 uint8_t random_session_key[16] = { 0, };
634 DATA_BLOB session_key = data_blob(NULL, 0);
636 NTSTATUS nt_status = NT_STATUS_OK;
638 nt_status = dcesrv_fetch_session_key(dce_call->conn, &session_key);
639 if (NT_STATUS_EQUAL(nt_status, NT_STATUS_NO_USER_SESSION_KEY)) {
640 DEBUG(3,("samr: failed to get session key: %s "
641 "=> use a random session key\n",
642 nt_errstr(nt_status)));
645 * Windows just uses a random key
647 generate_random_buffer(random_session_key,
648 sizeof(random_session_key));
649 session_key = data_blob_const(random_session_key,
650 sizeof(random_session_key));
651 nt_status = NT_STATUS_OK;
653 if (!NT_STATUS_IS_OK(nt_status)) {
657 if (lm_pwd_hash != NULL) {
658 in = data_blob_const(lm_pwd_hash, 16);
659 out = data_blob_talloc_zero(mem_ctx, 16);
661 sess_crypt_blob(&out, &in, &session_key, false);
663 d_lm_pwd_hash = (struct samr_Password *) out.data;
665 if (nt_pwd_hash != NULL) {
666 in = data_blob_const(nt_pwd_hash, 16);
667 out = data_blob_talloc_zero(mem_ctx, 16);
669 sess_crypt_blob(&out, &in, &session_key, false);
671 d_nt_pwd_hash = (struct samr_Password *) out.data;
674 if ((d_lm_pwd_hash != NULL) || (d_nt_pwd_hash != NULL)) {
675 nt_status = samdb_set_password(sam_ctx, mem_ctx, account_dn,
677 d_lm_pwd_hash, d_nt_pwd_hash,
678 NULL, NULL, /* this is a password set */