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 #include <gnutls/gnutls.h>
36 #include <gnutls/crypto.h>
38 static void log_password_change_event(struct imessaging_context *msg_ctx,
39 struct loadparm_context *lp_ctx,
40 const struct tsocket_address *remote_client_address,
41 const struct tsocket_address *local_server_address,
42 const char *auth_description,
43 const char *password_type,
44 const char *original_client_name,
45 const char *account_name_from_db,
50 * Forcing this via the NTLM auth structure is not ideal, but
51 * it is the most practical option right now, and ensures the
52 * logs are consistent, even if some elements are always NULL.
54 struct auth_usersupplied_info ui = {
58 .account_name = original_client_name,
59 .domain_name = lpcfg_sam_name(lp_ctx),
62 .account_name = account_name_from_db,
63 .domain_name = lpcfg_sam_name(lp_ctx),
65 .remote_host = remote_client_address,
66 .local_host = local_server_address,
67 .service_description = "SAMR Password Change",
68 .auth_description = auth_description,
69 .password_type = password_type,
72 log_authentication_event(msg_ctx,
77 ui.mapped.domain_name,
78 ui.mapped.account_name,
82 samr_ChangePasswordUser
84 So old it is just not worth implementing
85 because it does not supply a plaintext and so we can't do password
86 complexity checking and cannot update all the other password hashes.
89 NTSTATUS dcesrv_samr_ChangePasswordUser(struct dcesrv_call_state *dce_call,
91 struct samr_ChangePasswordUser *r)
93 return NT_STATUS_NOT_IMPLEMENTED;
97 samr_OemChangePasswordUser2
99 NTSTATUS dcesrv_samr_OemChangePasswordUser2(struct dcesrv_call_state *dce_call,
101 struct samr_OemChangePasswordUser2 *r)
103 struct auth_session_info *session_info =
104 dcesrv_call_session_info(dce_call);
105 NTSTATUS status = NT_STATUS_WRONG_PASSWORD;
106 DATA_BLOB new_password, new_unicode_password;
108 struct samr_CryptPassword *pwbuf = r->in.password;
109 struct ldb_context *sam_ctx;
110 struct ldb_dn *user_dn;
112 struct ldb_message **res;
113 const char * const attrs[] = { "objectSid", "dBCSPwd",
114 "userAccountControl",
116 "msDS-User-Account-Control-Computed",
117 "badPwdCount", "badPasswordTime",
120 struct samr_Password *lm_pwd;
121 DATA_BLOB lm_pwd_blob;
122 uint8_t new_lm_hash[16];
123 struct samr_Password lm_verifier;
124 size_t unicode_pw_len;
125 size_t converted_size = 0;
126 const char *user_samAccountName = NULL;
127 struct dom_sid *user_objectSid = NULL;
130 return NT_STATUS_INVALID_PARAMETER;
133 if (r->in.hash == NULL) {
134 return NT_STATUS_INVALID_PARAMETER;
137 /* this call can only work with lanman auth */
138 if (!lpcfg_lanman_auth(dce_call->conn->dce_ctx->lp_ctx)) {
139 return NT_STATUS_ACCESS_DISABLED_BY_POLICY_OTHER;
142 /* Connect to a SAMDB with system privileges for fetching the old pw
144 sam_ctx = samdb_connect(mem_ctx,
146 dce_call->conn->dce_ctx->lp_ctx,
147 system_session(dce_call->conn->dce_ctx->lp_ctx),
148 dce_call->conn->remote_address,
150 if (sam_ctx == NULL) {
151 return NT_STATUS_INVALID_SYSTEM_SERVICE;
154 /* we need the users dn and the domain dn (derived from the
155 user SID). We also need the current lm password hash in
156 order to decrypt the incoming password */
157 ret = gendb_search(sam_ctx,
158 mem_ctx, NULL, &res, attrs,
159 "(&(sAMAccountName=%s)(objectclass=user))",
160 ldb_binary_encode_string(mem_ctx, r->in.account->string));
162 status = NT_STATUS_NO_SUCH_USER; /* Converted to WRONG_PASSWORD below */
166 user_dn = res[0]->dn;
168 user_samAccountName = ldb_msg_find_attr_as_string(res[0], "samAccountName", NULL);
169 user_objectSid = samdb_result_dom_sid(res, res[0], "objectSid");
171 status = samdb_result_passwords(mem_ctx, dce_call->conn->dce_ctx->lp_ctx,
172 res[0], &lm_pwd, NULL);
173 if (!NT_STATUS_IS_OK(status)) {
175 } else if (!lm_pwd) {
176 status = NT_STATUS_WRONG_PASSWORD;
180 /* decrypt the password we have been given */
181 lm_pwd_blob = data_blob(lm_pwd->hash, sizeof(lm_pwd->hash));
182 arcfour_crypt_blob(pwbuf->data, 516, &lm_pwd_blob);
183 data_blob_free(&lm_pwd_blob);
185 if (!extract_pw_from_buffer(mem_ctx, pwbuf->data, &new_password)) {
186 DEBUG(3,("samr: failed to decode password buffer\n"));
187 authsam_update_bad_pwd_count(sam_ctx, res[0], ldb_get_default_basedn(sam_ctx));
188 status = NT_STATUS_WRONG_PASSWORD;
192 if (!convert_string_talloc_handle(mem_ctx, lpcfg_iconv_handle(dce_call->conn->dce_ctx->lp_ctx),
194 (const char *)new_password.data,
196 (void **)&new_pass, &converted_size)) {
197 DEBUG(3,("samr: failed to convert incoming password buffer to unix charset\n"));
198 authsam_update_bad_pwd_count(sam_ctx, res[0], ldb_get_default_basedn(sam_ctx));
199 status = NT_STATUS_WRONG_PASSWORD;
203 if (!convert_string_talloc_handle(mem_ctx, lpcfg_iconv_handle(dce_call->conn->dce_ctx->lp_ctx),
205 (const char *)new_password.data,
207 (void **)&new_unicode_password.data, &unicode_pw_len)) {
208 DEBUG(3,("samr: failed to convert incoming password buffer to UTF16 charset\n"));
209 authsam_update_bad_pwd_count(sam_ctx, res[0], ldb_get_default_basedn(sam_ctx));
210 status = NT_STATUS_WRONG_PASSWORD;
213 new_unicode_password.length = unicode_pw_len;
215 E_deshash(new_pass, new_lm_hash);
216 E_old_pw_hash(new_lm_hash, lm_pwd->hash, lm_verifier.hash);
217 if (memcmp(lm_verifier.hash, r->in.hash->hash, 16) != 0) {
218 authsam_update_bad_pwd_count(sam_ctx, res[0], ldb_get_default_basedn(sam_ctx));
219 status = NT_STATUS_WRONG_PASSWORD;
223 /* Connect to a SAMDB with user privileges for the password change */
224 sam_ctx = samdb_connect(mem_ctx,
226 dce_call->conn->dce_ctx->lp_ctx,
228 dce_call->conn->remote_address,
230 if (sam_ctx == NULL) {
231 return NT_STATUS_INVALID_SYSTEM_SERVICE;
234 /* Start transaction */
235 ret = ldb_transaction_start(sam_ctx);
236 if (ret != LDB_SUCCESS) {
237 DEBUG(1, ("Failed to start transaction: %s\n", ldb_errstring(sam_ctx)));
238 return NT_STATUS_TRANSACTION_ABORTED;
241 /* Performs the password modification. We pass the old hashes read out
242 * from the database since they were already checked against the user-
244 status = samdb_set_password(sam_ctx, mem_ctx,
246 &new_unicode_password,
248 lm_pwd, NULL, /* this is a user password change */
251 if (!NT_STATUS_IS_OK(status)) {
252 ldb_transaction_cancel(sam_ctx);
256 /* And this confirms it in a transaction commit */
257 ret = ldb_transaction_commit(sam_ctx);
258 if (ret != LDB_SUCCESS) {
259 DEBUG(1,("Failed to commit transaction to change password on %s: %s\n",
260 ldb_dn_get_linearized(user_dn),
261 ldb_errstring(sam_ctx)));
262 status = NT_STATUS_TRANSACTION_ABORTED;
266 status = NT_STATUS_OK;
270 log_password_change_event(dce_call->conn->msg_ctx,
271 dce_call->conn->dce_ctx->lp_ctx,
272 dce_call->conn->remote_address,
273 dce_call->conn->local_address,
274 "OemChangePasswordUser2",
275 "RC4/DES using LanMan-hash",
276 r->in.account->string,
280 if (NT_STATUS_IS_OK(status)) {
283 /* Only update the badPwdCount if we found the user */
284 if (NT_STATUS_EQUAL(status, NT_STATUS_WRONG_PASSWORD)) {
285 authsam_update_bad_pwd_count(sam_ctx, res[0], ldb_get_default_basedn(sam_ctx));
286 } else if (NT_STATUS_EQUAL(status, NT_STATUS_NO_SUCH_USER)) {
287 /* Don't give the game away: (don't allow anonymous users to prove the existence of usernames) */
288 status = NT_STATUS_WRONG_PASSWORD;
296 samr_ChangePasswordUser3
298 NTSTATUS dcesrv_samr_ChangePasswordUser3(struct dcesrv_call_state *dce_call,
300 struct samr_ChangePasswordUser3 *r)
302 struct auth_session_info *session_info =
303 dcesrv_call_session_info(dce_call);
304 NTSTATUS status = NT_STATUS_WRONG_PASSWORD;
305 DATA_BLOB new_password;
306 struct ldb_context *sam_ctx = NULL;
307 struct ldb_dn *user_dn = NULL;
309 struct ldb_message **res;
310 const char * const attrs[] = { "unicodePwd", "dBCSPwd",
311 "userAccountControl",
313 "msDS-User-Account-Control-Computed",
314 "badPwdCount", "badPasswordTime",
316 struct samr_Password *nt_pwd, *lm_pwd;
317 DATA_BLOB nt_pwd_blob;
318 struct samr_DomInfo1 *dominfo = NULL;
319 struct userPwdChangeFailureInformation *reject = NULL;
320 enum samPwdChangeReason reason = SAM_PWD_CHANGE_NO_ERROR;
321 uint8_t new_nt_hash[16], new_lm_hash[16];
322 struct samr_Password nt_verifier, lm_verifier;
323 const char *user_samAccountName = NULL;
324 struct dom_sid *user_objectSid = NULL;
325 enum ntlm_auth_level ntlm_auth_level
326 = lpcfg_ntlm_auth(dce_call->conn->dce_ctx->lp_ctx);
328 *r->out.dominfo = NULL;
329 *r->out.reject = NULL;
331 /* this call should be disabled without NTLM auth */
332 if (ntlm_auth_level == NTLM_AUTH_DISABLED) {
333 DBG_WARNING("NTLM password changes not"
334 "permitted by configuration.\n");
335 return NT_STATUS_NTLM_BLOCKED;
338 if (r->in.nt_password == NULL ||
339 r->in.nt_verifier == NULL) {
340 return NT_STATUS_INVALID_PARAMETER;
343 /* Connect to a SAMDB with system privileges for fetching the old pw
345 sam_ctx = samdb_connect(mem_ctx,
347 dce_call->conn->dce_ctx->lp_ctx,
348 system_session(dce_call->conn->dce_ctx->lp_ctx),
349 dce_call->conn->remote_address,
351 if (sam_ctx == NULL) {
352 return NT_STATUS_INVALID_SYSTEM_SERVICE;
355 /* we need the users dn and the domain dn (derived from the
356 user SID). We also need the current lm and nt password hashes
357 in order to decrypt the incoming passwords */
358 ret = gendb_search(sam_ctx,
359 mem_ctx, NULL, &res, attrs,
360 "(&(sAMAccountName=%s)(objectclass=user))",
361 ldb_binary_encode_string(mem_ctx, r->in.account->string));
363 status = NT_STATUS_NO_SUCH_USER; /* Converted to WRONG_PASSWORD below */
367 user_dn = res[0]->dn;
368 user_samAccountName = ldb_msg_find_attr_as_string(res[0], "samAccountName", NULL);
369 user_objectSid = samdb_result_dom_sid(res, res[0], "objectSid");
371 status = samdb_result_passwords(mem_ctx, dce_call->conn->dce_ctx->lp_ctx,
372 res[0], &lm_pwd, &nt_pwd);
373 if (!NT_STATUS_IS_OK(status) ) {
378 status = NT_STATUS_WRONG_PASSWORD;
382 /* decrypt the password we have been given */
383 nt_pwd_blob = data_blob(nt_pwd->hash, sizeof(nt_pwd->hash));
384 arcfour_crypt_blob(r->in.nt_password->data, 516, &nt_pwd_blob);
385 data_blob_free(&nt_pwd_blob);
387 if (!extract_pw_from_buffer(mem_ctx, r->in.nt_password->data, &new_password)) {
388 DEBUG(3,("samr: failed to decode password buffer\n"));
389 status = NT_STATUS_WRONG_PASSWORD;
393 if (r->in.nt_verifier == NULL) {
394 status = NT_STATUS_WRONG_PASSWORD;
398 /* check NT verifier */
399 mdfour(new_nt_hash, new_password.data, new_password.length);
401 E_old_pw_hash(new_nt_hash, nt_pwd->hash, nt_verifier.hash);
402 if (memcmp(nt_verifier.hash, r->in.nt_verifier->hash, 16) != 0) {
403 status = NT_STATUS_WRONG_PASSWORD;
407 /* check LM verifier (really not needed as we just checked the
408 * much stronger NT hash, but the RPC-SAMR test checks for
410 if (lm_pwd && r->in.lm_verifier != NULL) {
412 size_t converted_size = 0;
414 if (!convert_string_talloc_handle(mem_ctx, lpcfg_iconv_handle(dce_call->conn->dce_ctx->lp_ctx),
416 (const char *)new_password.data,
418 (void **)&new_pass, &converted_size)) {
419 E_deshash(new_pass, new_lm_hash);
420 E_old_pw_hash(new_nt_hash, lm_pwd->hash, lm_verifier.hash);
421 if (memcmp(lm_verifier.hash, r->in.lm_verifier->hash, 16) != 0) {
422 status = NT_STATUS_WRONG_PASSWORD;
428 /* Connect to a SAMDB with user privileges for the password change */
429 sam_ctx = samdb_connect(mem_ctx,
431 dce_call->conn->dce_ctx->lp_ctx,
433 dce_call->conn->remote_address,
435 if (sam_ctx == NULL) {
436 return NT_STATUS_INVALID_SYSTEM_SERVICE;
439 ret = ldb_transaction_start(sam_ctx);
440 if (ret != LDB_SUCCESS) {
441 DEBUG(1, ("Failed to start transaction: %s\n", ldb_errstring(sam_ctx)));
442 return NT_STATUS_TRANSACTION_ABORTED;
445 /* Performs the password modification. We pass the old hashes read out
446 * from the database since they were already checked against the user-
448 status = samdb_set_password(sam_ctx, mem_ctx,
452 lm_pwd, nt_pwd, /* this is a user password change */
456 if (!NT_STATUS_IS_OK(status)) {
457 ldb_transaction_cancel(sam_ctx);
461 /* And this confirms it in a transaction commit */
462 ret = ldb_transaction_commit(sam_ctx);
463 if (ret != LDB_SUCCESS) {
464 DEBUG(1,("Failed to commit transaction to change password on %s: %s\n",
465 ldb_dn_get_linearized(user_dn),
466 ldb_errstring(sam_ctx)));
467 status = NT_STATUS_TRANSACTION_ABORTED;
471 status = NT_STATUS_OK;
475 log_password_change_event(dce_call->conn->msg_ctx,
476 dce_call->conn->dce_ctx->lp_ctx,
477 dce_call->conn->remote_address,
478 dce_call->conn->local_address,
479 "samr_ChangePasswordUser3",
480 "RC4/DES using NTLM-hash",
481 r->in.account->string,
485 if (NT_STATUS_IS_OK(status)) {
489 /* Only update the badPwdCount if we found the user */
490 if (NT_STATUS_EQUAL(status, NT_STATUS_WRONG_PASSWORD)) {
491 authsam_update_bad_pwd_count(sam_ctx, res[0], ldb_get_default_basedn(sam_ctx));
492 } else if (NT_STATUS_EQUAL(status, NT_STATUS_NO_SUCH_USER)) {
493 /* Don't give the game away: (don't allow anonymous users to prove the existence of usernames) */
494 status = NT_STATUS_WRONG_PASSWORD;
497 reject = talloc_zero(mem_ctx, struct userPwdChangeFailureInformation);
498 if (reject != NULL) {
499 reject->extendedFailureReason = reason;
501 *r->out.reject = reject;
504 *r->out.dominfo = dominfo;
510 samr_ChangePasswordUser2
512 easy - just a subset of samr_ChangePasswordUser3
514 NTSTATUS dcesrv_samr_ChangePasswordUser2(struct dcesrv_call_state *dce_call,
516 struct samr_ChangePasswordUser2 *r)
518 struct samr_ChangePasswordUser3 r2;
519 struct samr_DomInfo1 *dominfo = NULL;
520 struct userPwdChangeFailureInformation *reject = NULL;
522 r2.in.server = r->in.server;
523 r2.in.account = r->in.account;
524 r2.in.nt_password = r->in.nt_password;
525 r2.in.nt_verifier = r->in.nt_verifier;
526 r2.in.lm_change = r->in.lm_change;
527 r2.in.lm_password = r->in.lm_password;
528 r2.in.lm_verifier = r->in.lm_verifier;
529 r2.in.password3 = NULL;
530 r2.out.dominfo = &dominfo;
531 r2.out.reject = &reject;
533 return dcesrv_samr_ChangePasswordUser3(dce_call, mem_ctx, &r2);
538 set password via a samr_CryptPassword buffer
540 NTSTATUS samr_set_password(struct dcesrv_call_state *dce_call,
541 struct ldb_context *sam_ctx,
542 struct ldb_dn *account_dn, struct ldb_dn *domain_dn,
544 struct samr_CryptPassword *pwbuf)
547 DATA_BLOB new_password;
548 DATA_BLOB session_key = data_blob(NULL, 0);
550 nt_status = dcesrv_transport_session_key(dce_call, &session_key);
551 if (!NT_STATUS_IS_OK(nt_status)) {
552 DEBUG(3,("samr: failed to get session key: %s "
553 "=> NT_STATUS_WRONG_PASSWORD\n",
554 nt_errstr(nt_status)));
555 return NT_STATUS_WRONG_PASSWORD;
558 arcfour_crypt_blob(pwbuf->data, 516, &session_key);
560 if (!extract_pw_from_buffer(mem_ctx, pwbuf->data, &new_password)) {
561 DEBUG(3,("samr: failed to decode password buffer\n"));
562 return NT_STATUS_WRONG_PASSWORD;
565 /* set the password - samdb needs to know both the domain and user DNs,
566 so the domain password policy can be used */
567 return samdb_set_password(sam_ctx, mem_ctx,
568 account_dn, domain_dn,
571 NULL, NULL, /* This is a password set, not change */
577 set password via a samr_CryptPasswordEx buffer
579 NTSTATUS samr_set_password_ex(struct dcesrv_call_state *dce_call,
580 struct ldb_context *sam_ctx,
581 struct ldb_dn *account_dn,
582 struct ldb_dn *domain_dn,
584 struct samr_CryptPasswordEx *pwbuf)
587 DATA_BLOB new_password;
588 DATA_BLOB co_session_key;
589 DATA_BLOB session_key = data_blob(NULL, 0);
590 gnutls_hash_hd_t hash_hnd = NULL;
593 nt_status = dcesrv_transport_session_key(dce_call, &session_key);
594 if (!NT_STATUS_IS_OK(nt_status)) {
595 DEBUG(3,("samr: failed to get session key: %s "
596 "=> NT_STATUS_WRONG_PASSWORD\n",
597 nt_errstr(nt_status)));
598 return NT_STATUS_WRONG_PASSWORD;
601 co_session_key = data_blob_talloc(mem_ctx, NULL, 16);
602 if (!co_session_key.data) {
603 return NT_STATUS_NO_MEMORY;
606 rc = gnutls_hash_init(&hash_hnd, GNUTLS_DIG_MD5);
608 nt_status = NT_STATUS_NO_MEMORY;
612 rc = gnutls_hash(hash_hnd, &pwbuf->data[516], 16);
614 gnutls_hash_deinit(hash_hnd, NULL);
615 nt_status = NT_STATUS_INTERNAL_ERROR;
618 rc = gnutls_hash(hash_hnd, session_key.data, session_key.length);
620 gnutls_hash_deinit(hash_hnd, NULL);
621 nt_status = NT_STATUS_INTERNAL_ERROR;
624 gnutls_hash_deinit(hash_hnd, co_session_key.data);
626 arcfour_crypt_blob(pwbuf->data, 516, &co_session_key);
627 ZERO_ARRAY_LEN(co_session_key.data,
628 co_session_key.length);
630 if (!extract_pw_from_buffer(mem_ctx, pwbuf->data, &new_password)) {
631 DEBUG(3,("samr: failed to decode password buffer\n"));
632 nt_status = NT_STATUS_WRONG_PASSWORD;
636 /* set the password - samdb needs to know both the domain and user DNs,
637 so the domain password policy can be used */
638 nt_status = samdb_set_password(sam_ctx,
646 NULL, /* This is a password set, not change */
649 ZERO_ARRAY_LEN(new_password.data,
650 new_password.length);
657 set password via encrypted NT and LM hash buffers
659 NTSTATUS samr_set_password_buffers(struct dcesrv_call_state *dce_call,
660 struct ldb_context *sam_ctx,
661 struct ldb_dn *account_dn,
662 struct ldb_dn *domain_dn,
664 const uint8_t *lm_pwd_hash,
665 const uint8_t *nt_pwd_hash)
667 struct samr_Password *d_lm_pwd_hash = NULL, *d_nt_pwd_hash = NULL;
668 uint8_t random_session_key[16] = { 0, };
669 DATA_BLOB session_key = data_blob(NULL, 0);
671 NTSTATUS nt_status = NT_STATUS_OK;
673 nt_status = dcesrv_transport_session_key(dce_call, &session_key);
674 if (NT_STATUS_EQUAL(nt_status, NT_STATUS_NO_USER_SESSION_KEY)) {
675 DEBUG(3,("samr: failed to get session key: %s "
676 "=> use a random session key\n",
677 nt_errstr(nt_status)));
680 * Windows just uses a random key
682 generate_random_buffer(random_session_key,
683 sizeof(random_session_key));
684 session_key = data_blob_const(random_session_key,
685 sizeof(random_session_key));
686 nt_status = NT_STATUS_OK;
688 if (!NT_STATUS_IS_OK(nt_status)) {
692 if (lm_pwd_hash != NULL) {
693 in = data_blob_const(lm_pwd_hash, 16);
694 out = data_blob_talloc_zero(mem_ctx, 16);
696 sess_crypt_blob(&out, &in, &session_key, false);
698 d_lm_pwd_hash = (struct samr_Password *) out.data;
700 if (nt_pwd_hash != NULL) {
701 in = data_blob_const(nt_pwd_hash, 16);
702 out = data_blob_talloc_zero(mem_ctx, 16);
704 sess_crypt_blob(&out, &in, &session_key, false);
706 d_nt_pwd_hash = (struct samr_Password *) out.data;
709 if ((d_lm_pwd_hash != NULL) || (d_nt_pwd_hash != NULL)) {
710 nt_status = samdb_set_password(sam_ctx, mem_ctx, account_dn,
712 d_lm_pwd_hash, d_nt_pwd_hash,
713 NULL, NULL, /* this is a password set */