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 struct auth_session_info *session_info =
102 dcesrv_call_session_info(dce_call);
103 NTSTATUS status = NT_STATUS_WRONG_PASSWORD;
104 DATA_BLOB new_password, new_unicode_password;
106 struct samr_CryptPassword *pwbuf = r->in.password;
107 struct ldb_context *sam_ctx;
108 struct ldb_dn *user_dn;
110 struct ldb_message **res;
111 const char * const attrs[] = { "objectSid", "dBCSPwd",
112 "userAccountControl",
114 "msDS-User-Account-Control-Computed",
115 "badPwdCount", "badPasswordTime",
118 struct samr_Password *lm_pwd;
119 DATA_BLOB lm_pwd_blob;
120 uint8_t new_lm_hash[16];
121 struct samr_Password lm_verifier;
122 size_t unicode_pw_len;
123 size_t converted_size = 0;
124 const char *user_samAccountName = NULL;
125 struct dom_sid *user_objectSid = NULL;
128 return NT_STATUS_INVALID_PARAMETER;
131 if (r->in.hash == NULL) {
132 return NT_STATUS_INVALID_PARAMETER;
135 /* this call can only work with lanman auth */
136 if (!lpcfg_lanman_auth(dce_call->conn->dce_ctx->lp_ctx)) {
137 return NT_STATUS_ACCESS_DISABLED_BY_POLICY_OTHER;
140 /* Connect to a SAMDB with system privileges for fetching the old pw
142 sam_ctx = samdb_connect(mem_ctx,
144 dce_call->conn->dce_ctx->lp_ctx,
145 system_session(dce_call->conn->dce_ctx->lp_ctx),
146 dce_call->conn->remote_address,
148 if (sam_ctx == NULL) {
149 return NT_STATUS_INVALID_SYSTEM_SERVICE;
152 /* we need the users dn and the domain dn (derived from the
153 user SID). We also need the current lm password hash in
154 order to decrypt the incoming password */
155 ret = gendb_search(sam_ctx,
156 mem_ctx, NULL, &res, attrs,
157 "(&(sAMAccountName=%s)(objectclass=user))",
158 ldb_binary_encode_string(mem_ctx, r->in.account->string));
160 status = NT_STATUS_NO_SUCH_USER; /* Converted to WRONG_PASSWORD below */
164 user_dn = res[0]->dn;
166 user_samAccountName = ldb_msg_find_attr_as_string(res[0], "samAccountName", NULL);
167 user_objectSid = samdb_result_dom_sid(res, res[0], "objectSid");
169 status = samdb_result_passwords(mem_ctx, dce_call->conn->dce_ctx->lp_ctx,
170 res[0], &lm_pwd, NULL);
171 if (!NT_STATUS_IS_OK(status)) {
173 } else if (!lm_pwd) {
174 status = NT_STATUS_WRONG_PASSWORD;
178 /* decrypt the password we have been given */
179 lm_pwd_blob = data_blob(lm_pwd->hash, sizeof(lm_pwd->hash));
180 arcfour_crypt_blob(pwbuf->data, 516, &lm_pwd_blob);
181 data_blob_free(&lm_pwd_blob);
183 if (!extract_pw_from_buffer(mem_ctx, pwbuf->data, &new_password)) {
184 DEBUG(3,("samr: failed to decode password buffer\n"));
185 authsam_update_bad_pwd_count(sam_ctx, res[0], ldb_get_default_basedn(sam_ctx));
186 status = NT_STATUS_WRONG_PASSWORD;
190 if (!convert_string_talloc_handle(mem_ctx, lpcfg_iconv_handle(dce_call->conn->dce_ctx->lp_ctx),
192 (const char *)new_password.data,
194 (void **)&new_pass, &converted_size)) {
195 DEBUG(3,("samr: failed to convert incoming password buffer to unix charset\n"));
196 authsam_update_bad_pwd_count(sam_ctx, res[0], ldb_get_default_basedn(sam_ctx));
197 status = NT_STATUS_WRONG_PASSWORD;
201 if (!convert_string_talloc_handle(mem_ctx, lpcfg_iconv_handle(dce_call->conn->dce_ctx->lp_ctx),
203 (const char *)new_password.data,
205 (void **)&new_unicode_password.data, &unicode_pw_len)) {
206 DEBUG(3,("samr: failed to convert incoming password buffer to UTF16 charset\n"));
207 authsam_update_bad_pwd_count(sam_ctx, res[0], ldb_get_default_basedn(sam_ctx));
208 status = NT_STATUS_WRONG_PASSWORD;
211 new_unicode_password.length = unicode_pw_len;
213 E_deshash(new_pass, new_lm_hash);
214 E_old_pw_hash(new_lm_hash, lm_pwd->hash, lm_verifier.hash);
215 if (memcmp(lm_verifier.hash, r->in.hash->hash, 16) != 0) {
216 authsam_update_bad_pwd_count(sam_ctx, res[0], ldb_get_default_basedn(sam_ctx));
217 status = NT_STATUS_WRONG_PASSWORD;
221 /* Connect to a SAMDB with user privileges for the password change */
222 sam_ctx = samdb_connect(mem_ctx,
224 dce_call->conn->dce_ctx->lp_ctx,
226 dce_call->conn->remote_address,
228 if (sam_ctx == NULL) {
229 return NT_STATUS_INVALID_SYSTEM_SERVICE;
232 /* Start transaction */
233 ret = ldb_transaction_start(sam_ctx);
234 if (ret != LDB_SUCCESS) {
235 DEBUG(1, ("Failed to start transaction: %s\n", ldb_errstring(sam_ctx)));
236 return NT_STATUS_TRANSACTION_ABORTED;
239 /* Performs the password modification. We pass the old hashes read out
240 * from the database since they were already checked against the user-
242 status = samdb_set_password(sam_ctx, mem_ctx,
244 &new_unicode_password,
246 lm_pwd, NULL, /* this is a user password change */
249 if (!NT_STATUS_IS_OK(status)) {
250 ldb_transaction_cancel(sam_ctx);
254 /* And this confirms it in a transaction commit */
255 ret = ldb_transaction_commit(sam_ctx);
256 if (ret != LDB_SUCCESS) {
257 DEBUG(1,("Failed to commit transaction to change password on %s: %s\n",
258 ldb_dn_get_linearized(user_dn),
259 ldb_errstring(sam_ctx)));
260 status = NT_STATUS_TRANSACTION_ABORTED;
264 status = NT_STATUS_OK;
268 log_password_change_event(dce_call->conn->msg_ctx,
269 dce_call->conn->dce_ctx->lp_ctx,
270 dce_call->conn->remote_address,
271 dce_call->conn->local_address,
272 "OemChangePasswordUser2",
273 "RC4/DES using LanMan-hash",
274 r->in.account->string,
278 if (NT_STATUS_IS_OK(status)) {
281 /* Only update the badPwdCount if we found the user */
282 if (NT_STATUS_EQUAL(status, NT_STATUS_WRONG_PASSWORD)) {
283 authsam_update_bad_pwd_count(sam_ctx, res[0], ldb_get_default_basedn(sam_ctx));
284 } else if (NT_STATUS_EQUAL(status, NT_STATUS_NO_SUCH_USER)) {
285 /* Don't give the game away: (don't allow anonymous users to prove the existence of usernames) */
286 status = NT_STATUS_WRONG_PASSWORD;
294 samr_ChangePasswordUser3
296 NTSTATUS dcesrv_samr_ChangePasswordUser3(struct dcesrv_call_state *dce_call,
298 struct samr_ChangePasswordUser3 *r)
300 struct auth_session_info *session_info =
301 dcesrv_call_session_info(dce_call);
302 NTSTATUS status = NT_STATUS_WRONG_PASSWORD;
303 DATA_BLOB new_password;
304 struct ldb_context *sam_ctx = NULL;
305 struct ldb_dn *user_dn = NULL;
307 struct ldb_message **res;
308 const char * const attrs[] = { "unicodePwd", "dBCSPwd",
309 "userAccountControl",
311 "msDS-User-Account-Control-Computed",
312 "badPwdCount", "badPasswordTime",
314 struct samr_Password *nt_pwd, *lm_pwd;
315 DATA_BLOB nt_pwd_blob;
316 struct samr_DomInfo1 *dominfo = NULL;
317 struct userPwdChangeFailureInformation *reject = NULL;
318 enum samPwdChangeReason reason = SAM_PWD_CHANGE_NO_ERROR;
319 uint8_t new_nt_hash[16], new_lm_hash[16];
320 struct samr_Password nt_verifier, lm_verifier;
321 const char *user_samAccountName = NULL;
322 struct dom_sid *user_objectSid = NULL;
323 enum ntlm_auth_level ntlm_auth_level
324 = lpcfg_ntlm_auth(dce_call->conn->dce_ctx->lp_ctx);
326 *r->out.dominfo = NULL;
327 *r->out.reject = NULL;
329 /* this call should be disabled without NTLM auth */
330 if (ntlm_auth_level == NTLM_AUTH_DISABLED) {
331 DBG_WARNING("NTLM password changes not"
332 "permitted by configuration.\n");
333 return NT_STATUS_NTLM_BLOCKED;
336 if (r->in.nt_password == NULL ||
337 r->in.nt_verifier == NULL) {
338 return NT_STATUS_INVALID_PARAMETER;
341 /* Connect to a SAMDB with system privileges for fetching the old pw
343 sam_ctx = samdb_connect(mem_ctx,
345 dce_call->conn->dce_ctx->lp_ctx,
346 system_session(dce_call->conn->dce_ctx->lp_ctx),
347 dce_call->conn->remote_address,
349 if (sam_ctx == NULL) {
350 return NT_STATUS_INVALID_SYSTEM_SERVICE;
353 /* we need the users dn and the domain dn (derived from the
354 user SID). We also need the current lm and nt password hashes
355 in order to decrypt the incoming passwords */
356 ret = gendb_search(sam_ctx,
357 mem_ctx, NULL, &res, attrs,
358 "(&(sAMAccountName=%s)(objectclass=user))",
359 ldb_binary_encode_string(mem_ctx, r->in.account->string));
361 status = NT_STATUS_NO_SUCH_USER; /* Converted to WRONG_PASSWORD below */
365 user_dn = res[0]->dn;
366 user_samAccountName = ldb_msg_find_attr_as_string(res[0], "samAccountName", NULL);
367 user_objectSid = samdb_result_dom_sid(res, res[0], "objectSid");
369 status = samdb_result_passwords(mem_ctx, dce_call->conn->dce_ctx->lp_ctx,
370 res[0], &lm_pwd, &nt_pwd);
371 if (!NT_STATUS_IS_OK(status) ) {
376 status = NT_STATUS_WRONG_PASSWORD;
380 /* decrypt the password we have been given */
381 nt_pwd_blob = data_blob(nt_pwd->hash, sizeof(nt_pwd->hash));
382 arcfour_crypt_blob(r->in.nt_password->data, 516, &nt_pwd_blob);
383 data_blob_free(&nt_pwd_blob);
385 if (!extract_pw_from_buffer(mem_ctx, r->in.nt_password->data, &new_password)) {
386 DEBUG(3,("samr: failed to decode password buffer\n"));
387 status = NT_STATUS_WRONG_PASSWORD;
391 if (r->in.nt_verifier == NULL) {
392 status = NT_STATUS_WRONG_PASSWORD;
396 /* check NT verifier */
397 mdfour(new_nt_hash, new_password.data, new_password.length);
399 E_old_pw_hash(new_nt_hash, nt_pwd->hash, nt_verifier.hash);
400 if (memcmp(nt_verifier.hash, r->in.nt_verifier->hash, 16) != 0) {
401 status = NT_STATUS_WRONG_PASSWORD;
405 /* check LM verifier (really not needed as we just checked the
406 * much stronger NT hash, but the RPC-SAMR test checks for
408 if (lm_pwd && r->in.lm_verifier != NULL) {
410 size_t converted_size = 0;
412 if (!convert_string_talloc_handle(mem_ctx, lpcfg_iconv_handle(dce_call->conn->dce_ctx->lp_ctx),
414 (const char *)new_password.data,
416 (void **)&new_pass, &converted_size)) {
417 E_deshash(new_pass, new_lm_hash);
418 E_old_pw_hash(new_nt_hash, lm_pwd->hash, lm_verifier.hash);
419 if (memcmp(lm_verifier.hash, r->in.lm_verifier->hash, 16) != 0) {
420 status = NT_STATUS_WRONG_PASSWORD;
426 /* Connect to a SAMDB with user privileges for the password change */
427 sam_ctx = samdb_connect(mem_ctx,
429 dce_call->conn->dce_ctx->lp_ctx,
431 dce_call->conn->remote_address,
433 if (sam_ctx == NULL) {
434 return NT_STATUS_INVALID_SYSTEM_SERVICE;
437 ret = ldb_transaction_start(sam_ctx);
438 if (ret != LDB_SUCCESS) {
439 DEBUG(1, ("Failed to start transaction: %s\n", ldb_errstring(sam_ctx)));
440 return NT_STATUS_TRANSACTION_ABORTED;
443 /* Performs the password modification. We pass the old hashes read out
444 * from the database since they were already checked against the user-
446 status = samdb_set_password(sam_ctx, mem_ctx,
450 lm_pwd, nt_pwd, /* this is a user password change */
454 if (!NT_STATUS_IS_OK(status)) {
455 ldb_transaction_cancel(sam_ctx);
459 /* And this confirms it in a transaction commit */
460 ret = ldb_transaction_commit(sam_ctx);
461 if (ret != LDB_SUCCESS) {
462 DEBUG(1,("Failed to commit transaction to change password on %s: %s\n",
463 ldb_dn_get_linearized(user_dn),
464 ldb_errstring(sam_ctx)));
465 status = NT_STATUS_TRANSACTION_ABORTED;
469 status = NT_STATUS_OK;
473 log_password_change_event(dce_call->conn->msg_ctx,
474 dce_call->conn->dce_ctx->lp_ctx,
475 dce_call->conn->remote_address,
476 dce_call->conn->local_address,
477 "samr_ChangePasswordUser3",
478 "RC4/DES using NTLM-hash",
479 r->in.account->string,
483 if (NT_STATUS_IS_OK(status)) {
487 /* Only update the badPwdCount if we found the user */
488 if (NT_STATUS_EQUAL(status, NT_STATUS_WRONG_PASSWORD)) {
489 authsam_update_bad_pwd_count(sam_ctx, res[0], ldb_get_default_basedn(sam_ctx));
490 } else if (NT_STATUS_EQUAL(status, NT_STATUS_NO_SUCH_USER)) {
491 /* Don't give the game away: (don't allow anonymous users to prove the existence of usernames) */
492 status = NT_STATUS_WRONG_PASSWORD;
495 reject = talloc_zero(mem_ctx, struct userPwdChangeFailureInformation);
496 if (reject != NULL) {
497 reject->extendedFailureReason = reason;
499 *r->out.reject = reject;
502 *r->out.dominfo = dominfo;
508 samr_ChangePasswordUser2
510 easy - just a subset of samr_ChangePasswordUser3
512 NTSTATUS dcesrv_samr_ChangePasswordUser2(struct dcesrv_call_state *dce_call,
514 struct samr_ChangePasswordUser2 *r)
516 struct samr_ChangePasswordUser3 r2;
517 struct samr_DomInfo1 *dominfo = NULL;
518 struct userPwdChangeFailureInformation *reject = NULL;
520 r2.in.server = r->in.server;
521 r2.in.account = r->in.account;
522 r2.in.nt_password = r->in.nt_password;
523 r2.in.nt_verifier = r->in.nt_verifier;
524 r2.in.lm_change = r->in.lm_change;
525 r2.in.lm_password = r->in.lm_password;
526 r2.in.lm_verifier = r->in.lm_verifier;
527 r2.in.password3 = NULL;
528 r2.out.dominfo = &dominfo;
529 r2.out.reject = &reject;
531 return dcesrv_samr_ChangePasswordUser3(dce_call, mem_ctx, &r2);
536 set password via a samr_CryptPassword buffer
538 NTSTATUS samr_set_password(struct dcesrv_call_state *dce_call,
539 struct ldb_context *sam_ctx,
540 struct ldb_dn *account_dn, struct ldb_dn *domain_dn,
542 struct samr_CryptPassword *pwbuf)
545 DATA_BLOB new_password;
546 DATA_BLOB session_key = data_blob(NULL, 0);
548 nt_status = dcesrv_fetch_session_key(dce_call->conn, &session_key);
549 if (!NT_STATUS_IS_OK(nt_status)) {
550 DEBUG(3,("samr: failed to get session key: %s "
551 "=> NT_STATUS_WRONG_PASSWORD\n",
552 nt_errstr(nt_status)));
553 return NT_STATUS_WRONG_PASSWORD;
556 arcfour_crypt_blob(pwbuf->data, 516, &session_key);
558 if (!extract_pw_from_buffer(mem_ctx, pwbuf->data, &new_password)) {
559 DEBUG(3,("samr: failed to decode password buffer\n"));
560 return NT_STATUS_WRONG_PASSWORD;
563 /* set the password - samdb needs to know both the domain and user DNs,
564 so the domain password policy can be used */
565 return samdb_set_password(sam_ctx, mem_ctx,
566 account_dn, domain_dn,
569 NULL, NULL, /* This is a password set, not change */
575 set password via a samr_CryptPasswordEx buffer
577 NTSTATUS samr_set_password_ex(struct dcesrv_call_state *dce_call,
578 struct ldb_context *sam_ctx,
579 struct ldb_dn *account_dn,
580 struct ldb_dn *domain_dn,
582 struct samr_CryptPasswordEx *pwbuf)
585 DATA_BLOB new_password;
586 DATA_BLOB co_session_key;
587 DATA_BLOB session_key = data_blob(NULL, 0);
590 nt_status = dcesrv_fetch_session_key(dce_call->conn, &session_key);
591 if (!NT_STATUS_IS_OK(nt_status)) {
592 DEBUG(3,("samr: failed to get session key: %s "
593 "=> NT_STATUS_WRONG_PASSWORD\n",
594 nt_errstr(nt_status)));
595 return NT_STATUS_WRONG_PASSWORD;
598 co_session_key = data_blob_talloc(mem_ctx, NULL, 16);
599 if (!co_session_key.data) {
600 return NT_STATUS_NO_MEMORY;
604 MD5Update(&ctx, &pwbuf->data[516], 16);
605 MD5Update(&ctx, session_key.data, session_key.length);
606 MD5Final(co_session_key.data, &ctx);
608 arcfour_crypt_blob(pwbuf->data, 516, &co_session_key);
610 if (!extract_pw_from_buffer(mem_ctx, pwbuf->data, &new_password)) {
611 DEBUG(3,("samr: failed to decode password buffer\n"));
612 return NT_STATUS_WRONG_PASSWORD;
615 /* set the password - samdb needs to know both the domain and user DNs,
616 so the domain password policy can be used */
617 return samdb_set_password(sam_ctx, mem_ctx,
618 account_dn, domain_dn,
621 NULL, NULL, /* This is a password set, not change */
626 set password via encrypted NT and LM hash buffers
628 NTSTATUS samr_set_password_buffers(struct dcesrv_call_state *dce_call,
629 struct ldb_context *sam_ctx,
630 struct ldb_dn *account_dn,
631 struct ldb_dn *domain_dn,
633 const uint8_t *lm_pwd_hash,
634 const uint8_t *nt_pwd_hash)
636 struct samr_Password *d_lm_pwd_hash = NULL, *d_nt_pwd_hash = NULL;
637 uint8_t random_session_key[16] = { 0, };
638 DATA_BLOB session_key = data_blob(NULL, 0);
640 NTSTATUS nt_status = NT_STATUS_OK;
642 nt_status = dcesrv_fetch_session_key(dce_call->conn, &session_key);
643 if (NT_STATUS_EQUAL(nt_status, NT_STATUS_NO_USER_SESSION_KEY)) {
644 DEBUG(3,("samr: failed to get session key: %s "
645 "=> use a random session key\n",
646 nt_errstr(nt_status)));
649 * Windows just uses a random key
651 generate_random_buffer(random_session_key,
652 sizeof(random_session_key));
653 session_key = data_blob_const(random_session_key,
654 sizeof(random_session_key));
655 nt_status = NT_STATUS_OK;
657 if (!NT_STATUS_IS_OK(nt_status)) {
661 if (lm_pwd_hash != NULL) {
662 in = data_blob_const(lm_pwd_hash, 16);
663 out = data_blob_talloc_zero(mem_ctx, 16);
665 sess_crypt_blob(&out, &in, &session_key, false);
667 d_lm_pwd_hash = (struct samr_Password *) out.data;
669 if (nt_pwd_hash != NULL) {
670 in = data_blob_const(nt_pwd_hash, 16);
671 out = data_blob_talloc_zero(mem_ctx, 16);
673 sess_crypt_blob(&out, &in, &session_key, false);
675 d_nt_pwd_hash = (struct samr_Password *) out.data;
678 if ((d_lm_pwd_hash != NULL) || (d_nt_pwd_hash != NULL)) {
679 nt_status = samdb_set_password(sam_ctx, mem_ctx, account_dn,
681 d_lm_pwd_hash, d_nt_pwd_hash,
682 NULL, NULL, /* this is a password set */