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,
73 ui.mapped.domain_name,
74 ui.mapped.account_name,
79 samr_ChangePasswordUser
81 So old it is just not worth implementing
82 because it does not supply a plaintext and so we can't do password
83 complexity checking and cannot update all the other password hashes.
86 NTSTATUS dcesrv_samr_ChangePasswordUser(struct dcesrv_call_state *dce_call,
88 struct samr_ChangePasswordUser *r)
90 return NT_STATUS_NOT_IMPLEMENTED;
94 samr_OemChangePasswordUser2
96 NTSTATUS dcesrv_samr_OemChangePasswordUser2(struct dcesrv_call_state *dce_call,
98 struct samr_OemChangePasswordUser2 *r)
100 NTSTATUS status = NT_STATUS_WRONG_PASSWORD;
101 DATA_BLOB new_password, new_unicode_password;
103 struct samr_CryptPassword *pwbuf = r->in.password;
104 struct ldb_context *sam_ctx;
105 struct ldb_dn *user_dn;
107 struct ldb_message **res;
108 const char * const attrs[] = { "objectSid", "dBCSPwd",
109 "userAccountControl",
110 "msDS-User-Account-Control-Computed",
111 "badPwdCount", "badPasswordTime",
114 struct samr_Password *lm_pwd;
115 DATA_BLOB lm_pwd_blob;
116 uint8_t new_lm_hash[16];
117 struct samr_Password lm_verifier;
118 size_t unicode_pw_len;
119 size_t converted_size = 0;
120 const char *user_samAccountName = NULL;
121 struct dom_sid *user_objectSid = NULL;
124 return NT_STATUS_INVALID_PARAMETER;
127 if (r->in.hash == NULL) {
128 return NT_STATUS_INVALID_PARAMETER;
131 /* this call can only work with lanman auth */
132 if (!lpcfg_lanman_auth(dce_call->conn->dce_ctx->lp_ctx)) {
133 return NT_STATUS_ACCESS_DISABLED_BY_POLICY_OTHER;
136 /* Connect to a SAMDB with system privileges for fetching the old pw
138 sam_ctx = samdb_connect(mem_ctx,
140 dce_call->conn->dce_ctx->lp_ctx,
141 system_session(dce_call->conn->dce_ctx->lp_ctx),
142 dce_call->conn->remote_address,
144 if (sam_ctx == NULL) {
145 return NT_STATUS_INVALID_SYSTEM_SERVICE;
148 /* we need the users dn and the domain dn (derived from the
149 user SID). We also need the current lm password hash in
150 order to decrypt the incoming password */
151 ret = gendb_search(sam_ctx,
152 mem_ctx, NULL, &res, attrs,
153 "(&(sAMAccountName=%s)(objectclass=user))",
154 ldb_binary_encode_string(mem_ctx, r->in.account->string));
156 status = NT_STATUS_NO_SUCH_USER; /* Converted to WRONG_PASSWORD below */
160 user_dn = res[0]->dn;
162 user_samAccountName = ldb_msg_find_attr_as_string(res[0], "samAccountName", NULL);
163 user_objectSid = samdb_result_dom_sid(res, res[0], "objectSid");
165 status = samdb_result_passwords(mem_ctx, dce_call->conn->dce_ctx->lp_ctx,
166 res[0], &lm_pwd, NULL);
167 if (!NT_STATUS_IS_OK(status)) {
169 } else if (!lm_pwd) {
170 status = NT_STATUS_WRONG_PASSWORD;
174 /* decrypt the password we have been given */
175 lm_pwd_blob = data_blob(lm_pwd->hash, sizeof(lm_pwd->hash));
176 arcfour_crypt_blob(pwbuf->data, 516, &lm_pwd_blob);
177 data_blob_free(&lm_pwd_blob);
179 if (!extract_pw_from_buffer(mem_ctx, pwbuf->data, &new_password)) {
180 DEBUG(3,("samr: failed to decode password buffer\n"));
181 authsam_update_bad_pwd_count(sam_ctx, res[0], ldb_get_default_basedn(sam_ctx));
182 status = NT_STATUS_WRONG_PASSWORD;
186 if (!convert_string_talloc_handle(mem_ctx, lpcfg_iconv_handle(dce_call->conn->dce_ctx->lp_ctx),
188 (const char *)new_password.data,
190 (void **)&new_pass, &converted_size)) {
191 DEBUG(3,("samr: failed to convert incoming password buffer to unix charset\n"));
192 authsam_update_bad_pwd_count(sam_ctx, res[0], ldb_get_default_basedn(sam_ctx));
193 status = NT_STATUS_WRONG_PASSWORD;
197 if (!convert_string_talloc_handle(mem_ctx, lpcfg_iconv_handle(dce_call->conn->dce_ctx->lp_ctx),
199 (const char *)new_password.data,
201 (void **)&new_unicode_password.data, &unicode_pw_len)) {
202 DEBUG(3,("samr: failed to convert incoming password buffer to UTF16 charset\n"));
203 authsam_update_bad_pwd_count(sam_ctx, res[0], ldb_get_default_basedn(sam_ctx));
204 status = NT_STATUS_WRONG_PASSWORD;
207 new_unicode_password.length = unicode_pw_len;
209 E_deshash(new_pass, new_lm_hash);
210 E_old_pw_hash(new_lm_hash, lm_pwd->hash, lm_verifier.hash);
211 if (memcmp(lm_verifier.hash, r->in.hash->hash, 16) != 0) {
212 authsam_update_bad_pwd_count(sam_ctx, res[0], ldb_get_default_basedn(sam_ctx));
213 status = NT_STATUS_WRONG_PASSWORD;
217 /* Connect to a SAMDB with user privileges for the password change */
218 sam_ctx = samdb_connect(mem_ctx,
220 dce_call->conn->dce_ctx->lp_ctx,
221 dce_call->conn->auth_state.session_info,
222 dce_call->conn->remote_address,
224 if (sam_ctx == NULL) {
225 return NT_STATUS_INVALID_SYSTEM_SERVICE;
228 /* Start transaction */
229 ret = ldb_transaction_start(sam_ctx);
230 if (ret != LDB_SUCCESS) {
231 DEBUG(1, ("Failed to start transaction: %s\n", ldb_errstring(sam_ctx)));
232 return NT_STATUS_TRANSACTION_ABORTED;
235 /* Performs the password modification. We pass the old hashes read out
236 * from the database since they were already checked against the user-
238 status = samdb_set_password(sam_ctx, mem_ctx,
240 &new_unicode_password,
242 lm_pwd, NULL, /* this is a user password change */
245 if (!NT_STATUS_IS_OK(status)) {
246 ldb_transaction_cancel(sam_ctx);
250 /* And this confirms it in a transaction commit */
251 ret = ldb_transaction_commit(sam_ctx);
252 if (ret != LDB_SUCCESS) {
253 DEBUG(1,("Failed to commit transaction to change password on %s: %s\n",
254 ldb_dn_get_linearized(user_dn),
255 ldb_errstring(sam_ctx)));
256 status = NT_STATUS_TRANSACTION_ABORTED;
260 status = NT_STATUS_OK;
264 log_password_change_event(dce_call->conn->msg_ctx,
265 dce_call->conn->dce_ctx->lp_ctx,
266 dce_call->conn->remote_address,
267 dce_call->conn->local_address,
268 "OemChangePasswordUser2",
269 "RC4/DES using LanMan-hash",
270 r->in.account->string,
274 if (NT_STATUS_IS_OK(status)) {
277 /* Only update the badPwdCount if we found the user */
278 if (NT_STATUS_EQUAL(status, NT_STATUS_WRONG_PASSWORD)) {
279 authsam_update_bad_pwd_count(sam_ctx, res[0], ldb_get_default_basedn(sam_ctx));
280 } else if (NT_STATUS_EQUAL(status, NT_STATUS_NO_SUCH_USER)) {
281 /* Don't give the game away: (don't allow anonymous users to prove the existence of usernames) */
282 status = NT_STATUS_WRONG_PASSWORD;
290 samr_ChangePasswordUser3
292 NTSTATUS dcesrv_samr_ChangePasswordUser3(struct dcesrv_call_state *dce_call,
294 struct samr_ChangePasswordUser3 *r)
296 NTSTATUS status = NT_STATUS_WRONG_PASSWORD;
297 DATA_BLOB new_password;
298 struct ldb_context *sam_ctx = NULL;
299 struct ldb_dn *user_dn = NULL;
301 struct ldb_message **res;
302 const char * const attrs[] = { "unicodePwd", "dBCSPwd",
303 "userAccountControl",
304 "msDS-User-Account-Control-Computed",
305 "badPwdCount", "badPasswordTime",
307 struct samr_Password *nt_pwd, *lm_pwd;
308 DATA_BLOB nt_pwd_blob;
309 struct samr_DomInfo1 *dominfo = NULL;
310 struct userPwdChangeFailureInformation *reject = NULL;
311 enum samPwdChangeReason reason = SAM_PWD_CHANGE_NO_ERROR;
312 uint8_t new_nt_hash[16], new_lm_hash[16];
313 struct samr_Password nt_verifier, lm_verifier;
314 const char *user_samAccountName = NULL;
315 struct dom_sid *user_objectSid = NULL;
316 enum ntlm_auth_level ntlm_auth_level
317 = lpcfg_ntlm_auth(dce_call->conn->dce_ctx->lp_ctx);
319 *r->out.dominfo = NULL;
320 *r->out.reject = NULL;
322 /* this call should be disabled without NTLM auth */
323 if (ntlm_auth_level == NTLM_AUTH_DISABLED) {
324 DBG_WARNING("NTLM password changes not"
325 "permitted by configuration.\n");
326 return NT_STATUS_NTLM_BLOCKED;
329 if (r->in.nt_password == NULL ||
330 r->in.nt_verifier == NULL) {
331 return NT_STATUS_INVALID_PARAMETER;
334 /* Connect to a SAMDB with system privileges for fetching the old pw
336 sam_ctx = samdb_connect(mem_ctx,
338 dce_call->conn->dce_ctx->lp_ctx,
339 system_session(dce_call->conn->dce_ctx->lp_ctx),
340 dce_call->conn->remote_address,
342 if (sam_ctx == NULL) {
343 return NT_STATUS_INVALID_SYSTEM_SERVICE;
346 /* we need the users dn and the domain dn (derived from the
347 user SID). We also need the current lm and nt password hashes
348 in order to decrypt the incoming passwords */
349 ret = gendb_search(sam_ctx,
350 mem_ctx, NULL, &res, attrs,
351 "(&(sAMAccountName=%s)(objectclass=user))",
352 ldb_binary_encode_string(mem_ctx, r->in.account->string));
354 status = NT_STATUS_NO_SUCH_USER; /* Converted to WRONG_PASSWORD below */
358 user_dn = res[0]->dn;
359 user_samAccountName = ldb_msg_find_attr_as_string(res[0], "samAccountName", NULL);
360 user_objectSid = samdb_result_dom_sid(res, res[0], "objectSid");
362 status = samdb_result_passwords(mem_ctx, dce_call->conn->dce_ctx->lp_ctx,
363 res[0], &lm_pwd, &nt_pwd);
364 if (!NT_STATUS_IS_OK(status) ) {
369 status = NT_STATUS_WRONG_PASSWORD;
373 /* decrypt the password we have been given */
374 nt_pwd_blob = data_blob(nt_pwd->hash, sizeof(nt_pwd->hash));
375 arcfour_crypt_blob(r->in.nt_password->data, 516, &nt_pwd_blob);
376 data_blob_free(&nt_pwd_blob);
378 if (!extract_pw_from_buffer(mem_ctx, r->in.nt_password->data, &new_password)) {
379 DEBUG(3,("samr: failed to decode password buffer\n"));
380 status = NT_STATUS_WRONG_PASSWORD;
384 if (r->in.nt_verifier == NULL) {
385 status = NT_STATUS_WRONG_PASSWORD;
389 /* check NT verifier */
390 mdfour(new_nt_hash, new_password.data, new_password.length);
392 E_old_pw_hash(new_nt_hash, nt_pwd->hash, nt_verifier.hash);
393 if (memcmp(nt_verifier.hash, r->in.nt_verifier->hash, 16) != 0) {
394 status = NT_STATUS_WRONG_PASSWORD;
398 /* check LM verifier (really not needed as we just checked the
399 * much stronger NT hash, but the RPC-SAMR test checks for
401 if (lm_pwd && r->in.lm_verifier != NULL) {
403 size_t converted_size = 0;
405 if (!convert_string_talloc_handle(mem_ctx, lpcfg_iconv_handle(dce_call->conn->dce_ctx->lp_ctx),
407 (const char *)new_password.data,
409 (void **)&new_pass, &converted_size)) {
410 E_deshash(new_pass, new_lm_hash);
411 E_old_pw_hash(new_nt_hash, lm_pwd->hash, lm_verifier.hash);
412 if (memcmp(lm_verifier.hash, r->in.lm_verifier->hash, 16) != 0) {
413 status = NT_STATUS_WRONG_PASSWORD;
419 /* Connect to a SAMDB with user privileges for the password change */
420 sam_ctx = samdb_connect(mem_ctx,
422 dce_call->conn->dce_ctx->lp_ctx,
423 dce_call->conn->auth_state.session_info,
424 dce_call->conn->remote_address,
426 if (sam_ctx == NULL) {
427 return NT_STATUS_INVALID_SYSTEM_SERVICE;
430 ret = ldb_transaction_start(sam_ctx);
431 if (ret != LDB_SUCCESS) {
432 DEBUG(1, ("Failed to start transaction: %s\n", ldb_errstring(sam_ctx)));
433 return NT_STATUS_TRANSACTION_ABORTED;
436 /* Performs the password modification. We pass the old hashes read out
437 * from the database since they were already checked against the user-
439 status = samdb_set_password(sam_ctx, mem_ctx,
443 lm_pwd, nt_pwd, /* this is a user password change */
447 if (!NT_STATUS_IS_OK(status)) {
448 ldb_transaction_cancel(sam_ctx);
452 /* And this confirms it in a transaction commit */
453 ret = ldb_transaction_commit(sam_ctx);
454 if (ret != LDB_SUCCESS) {
455 DEBUG(1,("Failed to commit transaction to change password on %s: %s\n",
456 ldb_dn_get_linearized(user_dn),
457 ldb_errstring(sam_ctx)));
458 status = NT_STATUS_TRANSACTION_ABORTED;
462 status = NT_STATUS_OK;
466 log_password_change_event(dce_call->conn->msg_ctx,
467 dce_call->conn->dce_ctx->lp_ctx,
468 dce_call->conn->remote_address,
469 dce_call->conn->local_address,
470 "samr_ChangePasswordUser3",
471 "RC4/DES using NTLM-hash",
472 r->in.account->string,
476 if (NT_STATUS_IS_OK(status)) {
480 /* Only update the badPwdCount if we found the user */
481 if (NT_STATUS_EQUAL(status, NT_STATUS_WRONG_PASSWORD)) {
482 authsam_update_bad_pwd_count(sam_ctx, res[0], ldb_get_default_basedn(sam_ctx));
483 } else if (NT_STATUS_EQUAL(status, NT_STATUS_NO_SUCH_USER)) {
484 /* Don't give the game away: (don't allow anonymous users to prove the existence of usernames) */
485 status = NT_STATUS_WRONG_PASSWORD;
488 reject = talloc_zero(mem_ctx, struct userPwdChangeFailureInformation);
489 if (reject != NULL) {
490 reject->extendedFailureReason = reason;
492 *r->out.reject = reject;
495 *r->out.dominfo = dominfo;
501 samr_ChangePasswordUser2
503 easy - just a subset of samr_ChangePasswordUser3
505 NTSTATUS dcesrv_samr_ChangePasswordUser2(struct dcesrv_call_state *dce_call,
507 struct samr_ChangePasswordUser2 *r)
509 struct samr_ChangePasswordUser3 r2;
510 struct samr_DomInfo1 *dominfo = NULL;
511 struct userPwdChangeFailureInformation *reject = NULL;
513 r2.in.server = r->in.server;
514 r2.in.account = r->in.account;
515 r2.in.nt_password = r->in.nt_password;
516 r2.in.nt_verifier = r->in.nt_verifier;
517 r2.in.lm_change = r->in.lm_change;
518 r2.in.lm_password = r->in.lm_password;
519 r2.in.lm_verifier = r->in.lm_verifier;
520 r2.in.password3 = NULL;
521 r2.out.dominfo = &dominfo;
522 r2.out.reject = &reject;
524 return dcesrv_samr_ChangePasswordUser3(dce_call, mem_ctx, &r2);
529 set password via a samr_CryptPassword buffer
531 NTSTATUS samr_set_password(struct dcesrv_call_state *dce_call,
532 struct ldb_context *sam_ctx,
533 struct ldb_dn *account_dn, struct ldb_dn *domain_dn,
535 struct samr_CryptPassword *pwbuf)
538 DATA_BLOB new_password;
539 DATA_BLOB session_key = data_blob(NULL, 0);
541 nt_status = dcesrv_fetch_session_key(dce_call->conn, &session_key);
542 if (!NT_STATUS_IS_OK(nt_status)) {
543 DEBUG(3,("samr: failed to get session key: %s "
544 "=> NT_STATUS_WRONG_PASSWORD\n",
545 nt_errstr(nt_status)));
546 return NT_STATUS_WRONG_PASSWORD;
549 arcfour_crypt_blob(pwbuf->data, 516, &session_key);
551 if (!extract_pw_from_buffer(mem_ctx, pwbuf->data, &new_password)) {
552 DEBUG(3,("samr: failed to decode password buffer\n"));
553 return NT_STATUS_WRONG_PASSWORD;
556 /* set the password - samdb needs to know both the domain and user DNs,
557 so the domain password policy can be used */
558 return samdb_set_password(sam_ctx, mem_ctx,
559 account_dn, domain_dn,
562 NULL, NULL, /* This is a password set, not change */
568 set password via a samr_CryptPasswordEx buffer
570 NTSTATUS samr_set_password_ex(struct dcesrv_call_state *dce_call,
571 struct ldb_context *sam_ctx,
572 struct ldb_dn *account_dn,
573 struct ldb_dn *domain_dn,
575 struct samr_CryptPasswordEx *pwbuf)
578 DATA_BLOB new_password;
579 DATA_BLOB co_session_key;
580 DATA_BLOB session_key = data_blob(NULL, 0);
583 nt_status = dcesrv_fetch_session_key(dce_call->conn, &session_key);
584 if (!NT_STATUS_IS_OK(nt_status)) {
585 DEBUG(3,("samr: failed to get session key: %s "
586 "=> NT_STATUS_WRONG_PASSWORD\n",
587 nt_errstr(nt_status)));
588 return NT_STATUS_WRONG_PASSWORD;
591 co_session_key = data_blob_talloc(mem_ctx, NULL, 16);
592 if (!co_session_key.data) {
593 return NT_STATUS_NO_MEMORY;
597 MD5Update(&ctx, &pwbuf->data[516], 16);
598 MD5Update(&ctx, session_key.data, session_key.length);
599 MD5Final(co_session_key.data, &ctx);
601 arcfour_crypt_blob(pwbuf->data, 516, &co_session_key);
603 if (!extract_pw_from_buffer(mem_ctx, pwbuf->data, &new_password)) {
604 DEBUG(3,("samr: failed to decode password buffer\n"));
605 return NT_STATUS_WRONG_PASSWORD;
608 /* set the password - samdb needs to know both the domain and user DNs,
609 so the domain password policy can be used */
610 return samdb_set_password(sam_ctx, mem_ctx,
611 account_dn, domain_dn,
614 NULL, NULL, /* This is a password set, not change */
619 set password via encrypted NT and LM hash buffers
621 NTSTATUS samr_set_password_buffers(struct dcesrv_call_state *dce_call,
622 struct ldb_context *sam_ctx,
623 struct ldb_dn *account_dn,
624 struct ldb_dn *domain_dn,
626 const uint8_t *lm_pwd_hash,
627 const uint8_t *nt_pwd_hash)
629 struct samr_Password *d_lm_pwd_hash = NULL, *d_nt_pwd_hash = NULL;
630 uint8_t random_session_key[16] = { 0, };
631 DATA_BLOB session_key = data_blob(NULL, 0);
633 NTSTATUS nt_status = NT_STATUS_OK;
635 nt_status = dcesrv_fetch_session_key(dce_call->conn, &session_key);
636 if (NT_STATUS_EQUAL(nt_status, NT_STATUS_NO_USER_SESSION_KEY)) {
637 DEBUG(3,("samr: failed to get session key: %s "
638 "=> use a random session key\n",
639 nt_errstr(nt_status)));
642 * Windows just uses a random key
644 generate_random_buffer(random_session_key,
645 sizeof(random_session_key));
646 session_key = data_blob_const(random_session_key,
647 sizeof(random_session_key));
648 nt_status = NT_STATUS_OK;
650 if (!NT_STATUS_IS_OK(nt_status)) {
654 if (lm_pwd_hash != NULL) {
655 in = data_blob_const(lm_pwd_hash, 16);
656 out = data_blob_talloc_zero(mem_ctx, 16);
658 sess_crypt_blob(&out, &in, &session_key, false);
660 d_lm_pwd_hash = (struct samr_Password *) out.data;
662 if (nt_pwd_hash != NULL) {
663 in = data_blob_const(nt_pwd_hash, 16);
664 out = data_blob_talloc_zero(mem_ctx, 16);
666 sess_crypt_blob(&out, &in, &session_key, false);
668 d_nt_pwd_hash = (struct samr_Password *) out.data;
671 if ((d_lm_pwd_hash != NULL) || (d_nt_pwd_hash != NULL)) {
672 nt_status = samdb_set_password(sam_ctx, mem_ctx, account_dn,
674 d_lm_pwd_hash, d_nt_pwd_hash,
675 NULL, NULL, /* this is a password set */