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/common/common.h"
26 #include "rpc_server/samr/dcesrv_samr.h"
27 #include "system/time.h"
28 #include "lib/crypto/md4.h"
29 #include "dsdb/samdb/samdb.h"
30 #include "auth/auth.h"
31 #include "libcli/auth/libcli_auth.h"
32 #include "../lib/util/util_ldb.h"
33 #include "rpc_server/samr/proto.h"
34 #include "auth/auth_sam.h"
35 #include "lib/param/loadparm.h"
36 #include "librpc/rpc/dcerpc_helper.h"
38 #include "lib/crypto/gnutls_helpers.h"
39 #include <gnutls/gnutls.h>
40 #include <gnutls/crypto.h>
42 static void log_password_change_event(struct imessaging_context *msg_ctx,
43 struct loadparm_context *lp_ctx,
44 const struct tsocket_address *remote_client_address,
45 const struct tsocket_address *local_server_address,
46 const char *auth_description,
47 const char *password_type,
48 const char *original_client_name,
49 const char *account_name_from_db,
54 * Forcing this via the NTLM auth structure is not ideal, but
55 * it is the most practical option right now, and ensures the
56 * logs are consistent, even if some elements are always NULL.
58 struct auth_usersupplied_info ui = {
61 .account_name = original_client_name,
62 .domain_name = lpcfg_sam_name(lp_ctx),
65 .account_name = account_name_from_db,
66 .domain_name = lpcfg_sam_name(lp_ctx),
68 .remote_host = remote_client_address,
69 .local_host = local_server_address,
70 .service_description = "SAMR Password Change",
71 .auth_description = auth_description,
72 .password_type = password_type,
75 log_authentication_event(msg_ctx,
80 ui.mapped.domain_name,
81 ui.mapped.account_name,
85 samr_ChangePasswordUser
87 So old it is just not worth implementing
88 because it does not supply a plaintext and so we can't do password
89 complexity checking and cannot update all the other password hashes.
92 NTSTATUS dcesrv_samr_ChangePasswordUser(struct dcesrv_call_state *dce_call,
94 struct samr_ChangePasswordUser *r)
96 return NT_STATUS_NOT_IMPLEMENTED;
100 samr_OemChangePasswordUser2
102 No longer implemented as it requires the LM hash
104 NTSTATUS dcesrv_samr_OemChangePasswordUser2(struct dcesrv_call_state *dce_call,
106 struct samr_OemChangePasswordUser2 *r)
108 return NT_STATUS_NOT_IMPLEMENTED;
113 samr_ChangePasswordUser3
115 NTSTATUS dcesrv_samr_ChangePasswordUser3(struct dcesrv_call_state *dce_call,
117 struct samr_ChangePasswordUser3 *r)
119 struct imessaging_context *imsg_ctx =
120 dcesrv_imessaging_context(dce_call->conn);
121 NTSTATUS status = NT_STATUS_WRONG_PASSWORD;
122 DATA_BLOB new_password;
123 struct ldb_context *sam_ctx = NULL;
124 struct ldb_dn *user_dn = NULL;
126 struct ldb_message **res;
127 const char * const attrs[] = { "unicodePwd", "dBCSPwd",
128 "userAccountControl",
130 "msDS-User-Account-Control-Computed",
131 "badPwdCount", "badPasswordTime",
133 struct samr_Password *nt_pwd;
134 struct samr_DomInfo1 *dominfo = NULL;
135 struct userPwdChangeFailureInformation *reject = NULL;
136 enum samPwdChangeReason reason = SAM_PWD_CHANGE_NO_ERROR;
137 uint8_t new_nt_hash[16];
138 struct samr_Password nt_verifier;
139 const char *user_samAccountName = NULL;
140 struct dom_sid *user_objectSid = NULL;
141 struct loadparm_context *lp_ctx = dce_call->conn->dce_ctx->lp_ctx;
142 enum ntlm_auth_level ntlm_auth_level
143 = lpcfg_ntlm_auth(lp_ctx);
144 gnutls_cipher_hd_t cipher_hnd = NULL;
145 gnutls_datum_t nt_session_key;
148 *r->out.dominfo = NULL;
149 *r->out.reject = NULL;
151 /* this call should be disabled without NTLM auth */
152 if (ntlm_auth_level == NTLM_AUTH_DISABLED) {
153 DBG_WARNING("NTLM password changes not"
154 "permitted by configuration.\n");
155 return NT_STATUS_NTLM_BLOCKED;
158 if (r->in.nt_password == NULL ||
159 r->in.nt_verifier == NULL) {
160 return NT_STATUS_INVALID_PARAMETER;
163 /* Connect to a SAMDB with system privileges for fetching the old pw
165 sam_ctx = dcesrv_samdb_connect_as_system(mem_ctx, dce_call);
166 if (sam_ctx == NULL) {
167 return NT_STATUS_INVALID_SYSTEM_SERVICE;
170 /* we need the users dn and the domain dn (derived from the
171 user SID). We also need the current lm and nt password hashes
172 in order to decrypt the incoming passwords */
173 ret = gendb_search(sam_ctx,
174 mem_ctx, NULL, &res, attrs,
175 "(&(sAMAccountName=%s)(objectclass=user))",
176 ldb_binary_encode_string(mem_ctx, r->in.account->string));
178 status = NT_STATUS_NO_SUCH_USER; /* Converted to WRONG_PASSWORD below */
182 user_dn = res[0]->dn;
183 user_samAccountName = ldb_msg_find_attr_as_string(res[0], "samAccountName", NULL);
184 user_objectSid = samdb_result_dom_sid(res, res[0], "objectSid");
186 status = samdb_result_passwords(mem_ctx, lp_ctx,
188 if (!NT_STATUS_IS_OK(status) ) {
193 status = NT_STATUS_WRONG_PASSWORD;
197 /* decrypt the password we have been given */
198 nt_session_key = (gnutls_datum_t) {
199 .data = nt_pwd->hash,
200 .size = sizeof(nt_pwd->hash),
203 rc = gnutls_cipher_init(&cipher_hnd,
204 GNUTLS_CIPHER_ARCFOUR_128,
208 status = gnutls_error_to_ntstatus(rc, NT_STATUS_CRYPTO_SYSTEM_INVALID);
212 rc = gnutls_cipher_decrypt(cipher_hnd,
213 r->in.nt_password->data,
215 gnutls_cipher_deinit(cipher_hnd);
217 status = gnutls_error_to_ntstatus(rc, NT_STATUS_CRYPTO_SYSTEM_INVALID);
221 if (!extract_pw_from_buffer(mem_ctx, r->in.nt_password->data, &new_password)) {
222 DEBUG(3,("samr: failed to decode password buffer\n"));
223 status = NT_STATUS_WRONG_PASSWORD;
227 if (r->in.nt_verifier == NULL) {
228 status = NT_STATUS_WRONG_PASSWORD;
232 /* check NT verifier */
233 mdfour(new_nt_hash, new_password.data, new_password.length);
235 E_old_pw_hash(new_nt_hash, nt_pwd->hash, nt_verifier.hash);
237 status = gnutls_error_to_ntstatus(rc, NT_STATUS_ACCESS_DISABLED_BY_POLICY_OTHER);
240 if (memcmp(nt_verifier.hash, r->in.nt_verifier->hash, 16) != 0) {
241 status = NT_STATUS_WRONG_PASSWORD;
245 /* Connect to a SAMDB with user privileges for the password change */
246 sam_ctx = dcesrv_samdb_connect_as_user(mem_ctx, dce_call);
247 if (sam_ctx == NULL) {
248 return NT_STATUS_INVALID_SYSTEM_SERVICE;
251 ret = ldb_transaction_start(sam_ctx);
252 if (ret != LDB_SUCCESS) {
253 DEBUG(1, ("Failed to start transaction: %s\n", ldb_errstring(sam_ctx)));
254 return NT_STATUS_TRANSACTION_ABORTED;
257 /* Performs the password modification. We pass the old hashes read out
258 * from the database since they were already checked against the user-
260 status = samdb_set_password(sam_ctx, mem_ctx,
264 DSDB_PASSWORD_CHECKED_AND_CORRECT,
268 if (!NT_STATUS_IS_OK(status)) {
269 ldb_transaction_cancel(sam_ctx);
273 /* And this confirms it in a transaction commit */
274 ret = ldb_transaction_commit(sam_ctx);
275 if (ret != LDB_SUCCESS) {
276 DEBUG(1,("Failed to commit transaction to change password on %s: %s\n",
277 ldb_dn_get_linearized(user_dn),
278 ldb_errstring(sam_ctx)));
279 status = NT_STATUS_TRANSACTION_ABORTED;
283 status = NT_STATUS_OK;
287 log_password_change_event(imsg_ctx,
289 dce_call->conn->remote_address,
290 dce_call->conn->local_address,
291 "samr_ChangePasswordUser3",
292 "RC4/DES using NTLM-hash",
293 r->in.account->string,
297 if (NT_STATUS_IS_OK(status)) {
301 /* Only update the badPwdCount if we found the user */
302 if (NT_STATUS_EQUAL(status, NT_STATUS_WRONG_PASSWORD)) {
303 authsam_update_bad_pwd_count(sam_ctx, res[0], ldb_get_default_basedn(sam_ctx));
304 } else if (NT_STATUS_EQUAL(status, NT_STATUS_NO_SUCH_USER)) {
305 /* Don't give the game away: (don't allow anonymous users to prove the existence of usernames) */
306 status = NT_STATUS_WRONG_PASSWORD;
309 reject = talloc_zero(mem_ctx, struct userPwdChangeFailureInformation);
310 if (reject != NULL) {
311 reject->extendedFailureReason = reason;
313 *r->out.reject = reject;
316 *r->out.dominfo = dominfo;
322 samr_ChangePasswordUser2
324 easy - just a subset of samr_ChangePasswordUser3
326 NTSTATUS dcesrv_samr_ChangePasswordUser2(struct dcesrv_call_state *dce_call,
328 struct samr_ChangePasswordUser2 *r)
330 struct samr_ChangePasswordUser3 r2;
331 struct samr_DomInfo1 *dominfo = NULL;
332 struct userPwdChangeFailureInformation *reject = NULL;
334 r2.in.server = r->in.server;
335 r2.in.account = r->in.account;
336 r2.in.nt_password = r->in.nt_password;
337 r2.in.nt_verifier = r->in.nt_verifier;
338 r2.in.lm_change = r->in.lm_change;
339 r2.in.lm_password = r->in.lm_password;
340 r2.in.lm_verifier = r->in.lm_verifier;
341 r2.in.password3 = NULL;
342 r2.out.dominfo = &dominfo;
343 r2.out.reject = &reject;
345 return dcesrv_samr_ChangePasswordUser3(dce_call, mem_ctx, &r2);
350 set password via a samr_CryptPassword buffer
352 NTSTATUS samr_set_password(struct dcesrv_call_state *dce_call,
353 struct ldb_context *sam_ctx,
354 struct ldb_dn *account_dn, struct ldb_dn *domain_dn,
356 struct samr_CryptPassword *pwbuf)
359 DATA_BLOB new_password;
360 DATA_BLOB session_key = data_blob(NULL, 0);
361 gnutls_cipher_hd_t cipher_hnd = NULL;
362 gnutls_datum_t _session_key;
363 struct auth_session_info *session_info =
364 dcesrv_call_session_info(dce_call);
365 struct loadparm_context *lp_ctx = dce_call->conn->dce_ctx->lp_ctx;
369 encrypted = dcerpc_is_transport_encrypted(session_info);
370 if (lpcfg_weak_crypto(lp_ctx) == SAMBA_WEAK_CRYPTO_DISALLOWED &&
372 return NT_STATUS_ACCESS_DENIED;
375 nt_status = dcesrv_transport_session_key(dce_call, &session_key);
376 if (!NT_STATUS_IS_OK(nt_status)) {
377 DBG_NOTICE("samr: failed to get session key: %s\n",
378 nt_errstr(nt_status));
382 _session_key = (gnutls_datum_t) {
383 .data = session_key.data,
384 .size = session_key.length,
388 * This is safe to support as we only have a session key
389 * over a SMB connection which we force to be encrypted.
391 GNUTLS_FIPS140_SET_LAX_MODE();
392 rc = gnutls_cipher_init(&cipher_hnd,
393 GNUTLS_CIPHER_ARCFOUR_128,
397 GNUTLS_FIPS140_SET_STRICT_MODE();
398 nt_status = gnutls_error_to_ntstatus(rc, NT_STATUS_CRYPTO_SYSTEM_INVALID);
402 rc = gnutls_cipher_decrypt(cipher_hnd,
405 gnutls_cipher_deinit(cipher_hnd);
406 GNUTLS_FIPS140_SET_STRICT_MODE();
408 nt_status = gnutls_error_to_ntstatus(rc, NT_STATUS_CRYPTO_SYSTEM_INVALID);
412 if (!extract_pw_from_buffer(mem_ctx, pwbuf->data, &new_password)) {
413 DEBUG(3,("samr: failed to decode password buffer\n"));
414 return NT_STATUS_WRONG_PASSWORD;
417 /* set the password - samdb needs to know both the domain and user DNs,
418 so the domain password policy can be used */
419 nt_status = samdb_set_password(sam_ctx,
434 set password via a samr_CryptPasswordEx buffer
436 NTSTATUS samr_set_password_ex(struct dcesrv_call_state *dce_call,
437 struct ldb_context *sam_ctx,
438 struct ldb_dn *account_dn,
439 struct ldb_dn *domain_dn,
441 struct samr_CryptPasswordEx *pwbuf)
443 struct loadparm_context *lp_ctx = dce_call->conn->dce_ctx->lp_ctx;
444 struct auth_session_info *session_info =
445 dcesrv_call_session_info(dce_call);
447 DATA_BLOB new_password;
449 /* The confounder is in the last 16 bytes of the buffer */
450 DATA_BLOB confounder = data_blob_const(&pwbuf->data[516], 16);
451 DATA_BLOB pw_data = data_blob_const(pwbuf->data, 516);
452 DATA_BLOB session_key = data_blob(NULL, 0);
456 nt_status = dcesrv_transport_session_key(dce_call, &session_key);
457 if (!NT_STATUS_IS_OK(nt_status)) {
458 DEBUG(3,("samr: failed to get session key: %s "
459 "=> NT_STATUS_WRONG_PASSWORD\n",
460 nt_errstr(nt_status)));
461 return NT_STATUS_WRONG_PASSWORD;
464 encrypted = dcerpc_is_transport_encrypted(session_info);
465 if (lpcfg_weak_crypto(lp_ctx) == SAMBA_WEAK_CRYPTO_DISALLOWED &&
467 return NT_STATUS_ACCESS_DENIED;
470 GNUTLS_FIPS140_SET_LAX_MODE();
471 rc = samba_gnutls_arcfour_confounded_md5(&confounder,
474 SAMBA_GNUTLS_DECRYPT);
475 GNUTLS_FIPS140_SET_STRICT_MODE();
477 nt_status = gnutls_error_to_ntstatus(rc, NT_STATUS_HASH_NOT_SUPPORTED);
481 if (!extract_pw_from_buffer(mem_ctx, pwbuf->data, &new_password)) {
482 DEBUG(3,("samr: failed to decode password buffer\n"));
483 nt_status = NT_STATUS_WRONG_PASSWORD;
487 /* set the password - samdb needs to know both the domain and user DNs,
488 so the domain password policy can be used */
489 nt_status = samdb_set_password(sam_ctx,
498 ZERO_ARRAY_LEN(new_password.data,
499 new_password.length);
506 set password via encrypted NT and LM hash buffers
508 NTSTATUS samr_set_password_buffers(struct dcesrv_call_state *dce_call,
509 struct ldb_context *sam_ctx,
510 struct ldb_dn *account_dn,
511 struct ldb_dn *domain_dn,
513 const uint8_t *lm_pwd_hash,
514 const uint8_t *nt_pwd_hash)
516 struct samr_Password *d_lm_pwd_hash = NULL, *d_nt_pwd_hash = NULL;
517 uint8_t random_session_key[16] = { 0, };
518 DATA_BLOB session_key = data_blob(NULL, 0);
520 NTSTATUS nt_status = NT_STATUS_OK;
523 nt_status = dcesrv_transport_session_key(dce_call, &session_key);
524 if (NT_STATUS_EQUAL(nt_status, NT_STATUS_NO_USER_SESSION_KEY)) {
525 DEBUG(3,("samr: failed to get session key: %s "
526 "=> use a random session key\n",
527 nt_errstr(nt_status)));
530 * Windows just uses a random key
532 generate_random_buffer(random_session_key,
533 sizeof(random_session_key));
534 session_key = data_blob_const(random_session_key,
535 sizeof(random_session_key));
536 nt_status = NT_STATUS_OK;
538 if (!NT_STATUS_IS_OK(nt_status)) {
542 if (nt_pwd_hash != NULL) {
543 in = data_blob_const(nt_pwd_hash, 16);
544 out = data_blob_talloc_zero(mem_ctx, 16);
546 rc = sess_crypt_blob(&out, &in, &session_key, SAMBA_GNUTLS_DECRYPT);
548 return gnutls_error_to_ntstatus(rc,
549 NT_STATUS_ACCESS_DISABLED_BY_POLICY_OTHER);
552 d_nt_pwd_hash = (struct samr_Password *) out.data;
555 if ((d_lm_pwd_hash != NULL) || (d_nt_pwd_hash != NULL)) {
556 nt_status = samdb_set_password(sam_ctx, mem_ctx, account_dn,