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, dce_call->event_ctx,
139 dce_call->conn->dce_ctx->lp_ctx,
140 system_session(dce_call->conn->dce_ctx->lp_ctx), 0);
141 if (sam_ctx == NULL) {
142 return NT_STATUS_INVALID_SYSTEM_SERVICE;
145 /* we need the users dn and the domain dn (derived from the
146 user SID). We also need the current lm password hash in
147 order to decrypt the incoming password */
148 ret = gendb_search(sam_ctx,
149 mem_ctx, NULL, &res, attrs,
150 "(&(sAMAccountName=%s)(objectclass=user))",
151 ldb_binary_encode_string(mem_ctx, r->in.account->string));
153 status = NT_STATUS_NO_SUCH_USER; /* Converted to WRONG_PASSWORD below */
157 user_dn = res[0]->dn;
159 user_samAccountName = ldb_msg_find_attr_as_string(res[0], "samAccountName", NULL);
160 user_objectSid = samdb_result_dom_sid(res, res[0], "objectSid");
162 status = samdb_result_passwords(mem_ctx, dce_call->conn->dce_ctx->lp_ctx,
163 res[0], &lm_pwd, NULL);
164 if (!NT_STATUS_IS_OK(status)) {
166 } else if (!lm_pwd) {
167 status = NT_STATUS_WRONG_PASSWORD;
171 /* decrypt the password we have been given */
172 lm_pwd_blob = data_blob(lm_pwd->hash, sizeof(lm_pwd->hash));
173 arcfour_crypt_blob(pwbuf->data, 516, &lm_pwd_blob);
174 data_blob_free(&lm_pwd_blob);
176 if (!extract_pw_from_buffer(mem_ctx, pwbuf->data, &new_password)) {
177 DEBUG(3,("samr: failed to decode password buffer\n"));
178 authsam_update_bad_pwd_count(sam_ctx, res[0], ldb_get_default_basedn(sam_ctx));
179 status = NT_STATUS_WRONG_PASSWORD;
183 if (!convert_string_talloc_handle(mem_ctx, lpcfg_iconv_handle(dce_call->conn->dce_ctx->lp_ctx),
185 (const char *)new_password.data,
187 (void **)&new_pass, &converted_size)) {
188 DEBUG(3,("samr: failed to convert incoming password buffer to unix charset\n"));
189 authsam_update_bad_pwd_count(sam_ctx, res[0], ldb_get_default_basedn(sam_ctx));
190 status = NT_STATUS_WRONG_PASSWORD;
194 if (!convert_string_talloc_handle(mem_ctx, lpcfg_iconv_handle(dce_call->conn->dce_ctx->lp_ctx),
196 (const char *)new_password.data,
198 (void **)&new_unicode_password.data, &unicode_pw_len)) {
199 DEBUG(3,("samr: failed to convert incoming password buffer to UTF16 charset\n"));
200 authsam_update_bad_pwd_count(sam_ctx, res[0], ldb_get_default_basedn(sam_ctx));
201 status = NT_STATUS_WRONG_PASSWORD;
204 new_unicode_password.length = unicode_pw_len;
206 E_deshash(new_pass, new_lm_hash);
207 E_old_pw_hash(new_lm_hash, lm_pwd->hash, lm_verifier.hash);
208 if (memcmp(lm_verifier.hash, r->in.hash->hash, 16) != 0) {
209 authsam_update_bad_pwd_count(sam_ctx, res[0], ldb_get_default_basedn(sam_ctx));
210 status = NT_STATUS_WRONG_PASSWORD;
214 /* Connect to a SAMDB with user privileges for the password change */
215 sam_ctx = samdb_connect(mem_ctx, dce_call->event_ctx,
216 dce_call->conn->dce_ctx->lp_ctx,
217 dce_call->conn->auth_state.session_info, 0);
218 if (sam_ctx == NULL) {
219 return NT_STATUS_INVALID_SYSTEM_SERVICE;
222 /* Start transaction */
223 ret = ldb_transaction_start(sam_ctx);
224 if (ret != LDB_SUCCESS) {
225 DEBUG(1, ("Failed to start transaction: %s\n", ldb_errstring(sam_ctx)));
226 return NT_STATUS_TRANSACTION_ABORTED;
229 /* Performs the password modification. We pass the old hashes read out
230 * from the database since they were already checked against the user-
232 status = samdb_set_password(sam_ctx, mem_ctx,
234 &new_unicode_password,
236 lm_pwd, NULL, /* this is a user password change */
239 if (!NT_STATUS_IS_OK(status)) {
240 ldb_transaction_cancel(sam_ctx);
244 /* And this confirms it in a transaction commit */
245 ret = ldb_transaction_commit(sam_ctx);
246 if (ret != LDB_SUCCESS) {
247 DEBUG(1,("Failed to commit transaction to change password on %s: %s\n",
248 ldb_dn_get_linearized(user_dn),
249 ldb_errstring(sam_ctx)));
250 status = NT_STATUS_TRANSACTION_ABORTED;
254 status = NT_STATUS_OK;
258 log_password_change_event(dce_call->conn->msg_ctx,
259 dce_call->conn->dce_ctx->lp_ctx,
260 dce_call->conn->remote_address,
261 dce_call->conn->local_address,
262 "OemChangePasswordUser2",
263 "RC4/DES using LanMan-hash",
264 r->in.account->string,
268 if (NT_STATUS_IS_OK(status)) {
271 /* Only update the badPwdCount if we found the user */
272 if (NT_STATUS_EQUAL(status, NT_STATUS_WRONG_PASSWORD)) {
273 authsam_update_bad_pwd_count(sam_ctx, res[0], ldb_get_default_basedn(sam_ctx));
274 } else if (NT_STATUS_EQUAL(status, NT_STATUS_NO_SUCH_USER)) {
275 /* Don't give the game away: (don't allow anonymous users to prove the existence of usernames) */
276 status = NT_STATUS_WRONG_PASSWORD;
284 samr_ChangePasswordUser3
286 NTSTATUS dcesrv_samr_ChangePasswordUser3(struct dcesrv_call_state *dce_call,
288 struct samr_ChangePasswordUser3 *r)
290 NTSTATUS status = NT_STATUS_WRONG_PASSWORD;
291 DATA_BLOB new_password;
292 struct ldb_context *sam_ctx = NULL;
293 struct ldb_dn *user_dn = NULL;
295 struct ldb_message **res;
296 const char * const attrs[] = { "unicodePwd", "dBCSPwd",
297 "userAccountControl",
298 "msDS-User-Account-Control-Computed",
299 "badPwdCount", "badPasswordTime",
301 struct samr_Password *nt_pwd, *lm_pwd;
302 DATA_BLOB nt_pwd_blob;
303 struct samr_DomInfo1 *dominfo = NULL;
304 struct userPwdChangeFailureInformation *reject = NULL;
305 enum samPwdChangeReason reason = SAM_PWD_CHANGE_NO_ERROR;
306 uint8_t new_nt_hash[16], new_lm_hash[16];
307 struct samr_Password nt_verifier, lm_verifier;
308 const char *user_samAccountName = NULL;
309 struct dom_sid *user_objectSid = NULL;
311 *r->out.dominfo = NULL;
312 *r->out.reject = NULL;
314 if (r->in.nt_password == NULL ||
315 r->in.nt_verifier == NULL) {
316 return NT_STATUS_INVALID_PARAMETER;
319 /* Connect to a SAMDB with system privileges for fetching the old pw
321 sam_ctx = samdb_connect(mem_ctx, dce_call->event_ctx,
322 dce_call->conn->dce_ctx->lp_ctx,
323 system_session(dce_call->conn->dce_ctx->lp_ctx), 0);
324 if (sam_ctx == NULL) {
325 return NT_STATUS_INVALID_SYSTEM_SERVICE;
328 /* we need the users dn and the domain dn (derived from the
329 user SID). We also need the current lm and nt password hashes
330 in order to decrypt the incoming passwords */
331 ret = gendb_search(sam_ctx,
332 mem_ctx, NULL, &res, attrs,
333 "(&(sAMAccountName=%s)(objectclass=user))",
334 ldb_binary_encode_string(mem_ctx, r->in.account->string));
336 status = NT_STATUS_NO_SUCH_USER; /* Converted to WRONG_PASSWORD below */
340 user_dn = res[0]->dn;
341 user_samAccountName = ldb_msg_find_attr_as_string(res[0], "samAccountName", NULL);
342 user_objectSid = samdb_result_dom_sid(res, res[0], "objectSid");
344 status = samdb_result_passwords(mem_ctx, dce_call->conn->dce_ctx->lp_ctx,
345 res[0], &lm_pwd, &nt_pwd);
346 if (!NT_STATUS_IS_OK(status) ) {
351 status = NT_STATUS_WRONG_PASSWORD;
355 /* decrypt the password we have been given */
356 nt_pwd_blob = data_blob(nt_pwd->hash, sizeof(nt_pwd->hash));
357 arcfour_crypt_blob(r->in.nt_password->data, 516, &nt_pwd_blob);
358 data_blob_free(&nt_pwd_blob);
360 if (!extract_pw_from_buffer(mem_ctx, r->in.nt_password->data, &new_password)) {
361 DEBUG(3,("samr: failed to decode password buffer\n"));
362 status = NT_STATUS_WRONG_PASSWORD;
366 if (r->in.nt_verifier == NULL) {
367 status = NT_STATUS_WRONG_PASSWORD;
371 /* check NT verifier */
372 mdfour(new_nt_hash, new_password.data, new_password.length);
374 E_old_pw_hash(new_nt_hash, nt_pwd->hash, nt_verifier.hash);
375 if (memcmp(nt_verifier.hash, r->in.nt_verifier->hash, 16) != 0) {
376 status = NT_STATUS_WRONG_PASSWORD;
380 /* check LM verifier (really not needed as we just checked the
381 * much stronger NT hash, but the RPC-SAMR test checks for
383 if (lm_pwd && r->in.lm_verifier != NULL) {
385 size_t converted_size = 0;
387 if (!convert_string_talloc_handle(mem_ctx, lpcfg_iconv_handle(dce_call->conn->dce_ctx->lp_ctx),
389 (const char *)new_password.data,
391 (void **)&new_pass, &converted_size)) {
392 E_deshash(new_pass, new_lm_hash);
393 E_old_pw_hash(new_nt_hash, lm_pwd->hash, lm_verifier.hash);
394 if (memcmp(lm_verifier.hash, r->in.lm_verifier->hash, 16) != 0) {
395 status = NT_STATUS_WRONG_PASSWORD;
401 /* Connect to a SAMDB with user privileges for the password change */
402 sam_ctx = samdb_connect(mem_ctx, dce_call->event_ctx,
403 dce_call->conn->dce_ctx->lp_ctx,
404 dce_call->conn->auth_state.session_info, 0);
405 if (sam_ctx == NULL) {
406 return NT_STATUS_INVALID_SYSTEM_SERVICE;
409 ret = ldb_transaction_start(sam_ctx);
410 if (ret != LDB_SUCCESS) {
411 DEBUG(1, ("Failed to start transaction: %s\n", ldb_errstring(sam_ctx)));
412 return NT_STATUS_TRANSACTION_ABORTED;
415 /* Performs the password modification. We pass the old hashes read out
416 * from the database since they were already checked against the user-
418 status = samdb_set_password(sam_ctx, mem_ctx,
422 lm_pwd, nt_pwd, /* this is a user password change */
426 if (!NT_STATUS_IS_OK(status)) {
427 ldb_transaction_cancel(sam_ctx);
431 /* And this confirms it in a transaction commit */
432 ret = ldb_transaction_commit(sam_ctx);
433 if (ret != LDB_SUCCESS) {
434 DEBUG(1,("Failed to commit transaction to change password on %s: %s\n",
435 ldb_dn_get_linearized(user_dn),
436 ldb_errstring(sam_ctx)));
437 status = NT_STATUS_TRANSACTION_ABORTED;
441 status = NT_STATUS_OK;
445 log_password_change_event(dce_call->conn->msg_ctx,
446 dce_call->conn->dce_ctx->lp_ctx,
447 dce_call->conn->remote_address,
448 dce_call->conn->local_address,
449 "samr_ChangePasswordUser3",
450 "RC4/DES using NTLM-hash",
451 r->in.account->string,
455 if (NT_STATUS_IS_OK(status)) {
459 /* Only update the badPwdCount if we found the user */
460 if (NT_STATUS_EQUAL(status, NT_STATUS_WRONG_PASSWORD)) {
461 authsam_update_bad_pwd_count(sam_ctx, res[0], ldb_get_default_basedn(sam_ctx));
462 } else if (NT_STATUS_EQUAL(status, NT_STATUS_NO_SUCH_USER)) {
463 /* Don't give the game away: (don't allow anonymous users to prove the existence of usernames) */
464 status = NT_STATUS_WRONG_PASSWORD;
467 reject = talloc_zero(mem_ctx, struct userPwdChangeFailureInformation);
468 if (reject != NULL) {
469 reject->extendedFailureReason = reason;
471 *r->out.reject = reject;
474 *r->out.dominfo = dominfo;
480 samr_ChangePasswordUser2
482 easy - just a subset of samr_ChangePasswordUser3
484 NTSTATUS dcesrv_samr_ChangePasswordUser2(struct dcesrv_call_state *dce_call,
486 struct samr_ChangePasswordUser2 *r)
488 struct samr_ChangePasswordUser3 r2;
489 struct samr_DomInfo1 *dominfo = NULL;
490 struct userPwdChangeFailureInformation *reject = NULL;
492 r2.in.server = r->in.server;
493 r2.in.account = r->in.account;
494 r2.in.nt_password = r->in.nt_password;
495 r2.in.nt_verifier = r->in.nt_verifier;
496 r2.in.lm_change = r->in.lm_change;
497 r2.in.lm_password = r->in.lm_password;
498 r2.in.lm_verifier = r->in.lm_verifier;
499 r2.in.password3 = NULL;
500 r2.out.dominfo = &dominfo;
501 r2.out.reject = &reject;
503 return dcesrv_samr_ChangePasswordUser3(dce_call, mem_ctx, &r2);
508 set password via a samr_CryptPassword buffer
510 NTSTATUS samr_set_password(struct dcesrv_call_state *dce_call,
511 struct ldb_context *sam_ctx,
512 struct ldb_dn *account_dn, struct ldb_dn *domain_dn,
514 struct samr_CryptPassword *pwbuf)
517 DATA_BLOB new_password;
518 DATA_BLOB session_key = data_blob(NULL, 0);
520 nt_status = dcesrv_fetch_session_key(dce_call->conn, &session_key);
521 if (!NT_STATUS_IS_OK(nt_status)) {
522 DEBUG(3,("samr: failed to get session key: %s "
523 "=> NT_STATUS_WRONG_PASSWORD\n",
524 nt_errstr(nt_status)));
525 return NT_STATUS_WRONG_PASSWORD;
528 arcfour_crypt_blob(pwbuf->data, 516, &session_key);
530 if (!extract_pw_from_buffer(mem_ctx, pwbuf->data, &new_password)) {
531 DEBUG(3,("samr: failed to decode password buffer\n"));
532 return NT_STATUS_WRONG_PASSWORD;
535 /* set the password - samdb needs to know both the domain and user DNs,
536 so the domain password policy can be used */
537 return samdb_set_password(sam_ctx, mem_ctx,
538 account_dn, domain_dn,
541 NULL, NULL, /* This is a password set, not change */
547 set password via a samr_CryptPasswordEx buffer
549 NTSTATUS samr_set_password_ex(struct dcesrv_call_state *dce_call,
550 struct ldb_context *sam_ctx,
551 struct ldb_dn *account_dn,
552 struct ldb_dn *domain_dn,
554 struct samr_CryptPasswordEx *pwbuf)
557 DATA_BLOB new_password;
558 DATA_BLOB co_session_key;
559 DATA_BLOB session_key = data_blob(NULL, 0);
562 nt_status = dcesrv_fetch_session_key(dce_call->conn, &session_key);
563 if (!NT_STATUS_IS_OK(nt_status)) {
564 DEBUG(3,("samr: failed to get session key: %s "
565 "=> NT_STATUS_WRONG_PASSWORD\n",
566 nt_errstr(nt_status)));
567 return NT_STATUS_WRONG_PASSWORD;
570 co_session_key = data_blob_talloc(mem_ctx, NULL, 16);
571 if (!co_session_key.data) {
572 return NT_STATUS_NO_MEMORY;
576 MD5Update(&ctx, &pwbuf->data[516], 16);
577 MD5Update(&ctx, session_key.data, session_key.length);
578 MD5Final(co_session_key.data, &ctx);
580 arcfour_crypt_blob(pwbuf->data, 516, &co_session_key);
582 if (!extract_pw_from_buffer(mem_ctx, pwbuf->data, &new_password)) {
583 DEBUG(3,("samr: failed to decode password buffer\n"));
584 return NT_STATUS_WRONG_PASSWORD;
587 /* set the password - samdb needs to know both the domain and user DNs,
588 so the domain password policy can be used */
589 return samdb_set_password(sam_ctx, mem_ctx,
590 account_dn, domain_dn,
593 NULL, NULL, /* This is a password set, not change */
598 set password via encrypted NT and LM hash buffers
600 NTSTATUS samr_set_password_buffers(struct dcesrv_call_state *dce_call,
601 struct ldb_context *sam_ctx,
602 struct ldb_dn *account_dn,
603 struct ldb_dn *domain_dn,
605 const uint8_t *lm_pwd_hash,
606 const uint8_t *nt_pwd_hash)
608 struct samr_Password *d_lm_pwd_hash = NULL, *d_nt_pwd_hash = NULL;
609 uint8_t random_session_key[16] = { 0, };
610 DATA_BLOB session_key = data_blob(NULL, 0);
612 NTSTATUS nt_status = NT_STATUS_OK;
614 nt_status = dcesrv_fetch_session_key(dce_call->conn, &session_key);
615 if (NT_STATUS_EQUAL(nt_status, NT_STATUS_NO_USER_SESSION_KEY)) {
616 DEBUG(3,("samr: failed to get session key: %s "
617 "=> use a random session key\n",
618 nt_errstr(nt_status)));
621 * Windows just uses a random key
623 generate_random_buffer(random_session_key,
624 sizeof(random_session_key));
625 session_key = data_blob_const(random_session_key,
626 sizeof(random_session_key));
627 nt_status = NT_STATUS_OK;
629 if (!NT_STATUS_IS_OK(nt_status)) {
633 if (lm_pwd_hash != NULL) {
634 in = data_blob_const(lm_pwd_hash, 16);
635 out = data_blob_talloc_zero(mem_ctx, 16);
637 sess_crypt_blob(&out, &in, &session_key, false);
639 d_lm_pwd_hash = (struct samr_Password *) out.data;
641 if (nt_pwd_hash != NULL) {
642 in = data_blob_const(nt_pwd_hash, 16);
643 out = data_blob_talloc_zero(mem_ctx, 16);
645 sess_crypt_blob(&out, &in, &session_key, false);
647 d_nt_pwd_hash = (struct samr_Password *) out.data;
650 if ((d_lm_pwd_hash != NULL) || (d_nt_pwd_hash != NULL)) {
651 nt_status = samdb_set_password(sam_ctx, mem_ctx, account_dn,
653 d_lm_pwd_hash, d_nt_pwd_hash,
654 NULL, NULL, /* this is a password set */