From 7beb2adb6de622c9bd01e4172d6cc3b987c36330 Mon Sep 17 00:00:00 2001 From: Jeremy Allison Date: Mon, 11 Jan 2021 12:11:35 -0800 Subject: [PATCH] WIP. Prototype patch for bug : https://bugzilla.samba.org/show_bug.cgi?id=14611 Signed-off-by: Jeremy Allison --- source3/auth/check_samsec.c | 77 +++++++++++++++++++++++++++++++++++++ 1 file changed, 77 insertions(+) diff --git a/source3/auth/check_samsec.c b/source3/auth/check_samsec.c index cfd32c5f5ba..613446ad3ed 100644 --- a/source3/auth/check_samsec.c +++ b/source3/auth/check_samsec.c @@ -379,6 +379,8 @@ NTSTATUS check_sam_security(const DATA_BLOB *challenge, const uint8_t *nt_pw; const uint8_t *lm_pw; uint32_t acct_ctrl; + char *mutex_name_by_user = NULL; + struct named_mutex *mtx = NULL; /* the returned struct gets kept on the server_info, by means of a steal further down */ @@ -418,6 +420,79 @@ NTSTATUS check_sam_security(const DATA_BLOB *challenge, challenge, lm_pw, nt_pw, user_info, &user_sess_key, &lm_sess_key); + /* + * We must re-load the sam acount information under a mutex + * lock to ensure we don't miss any concurrent account lockout + * changes. + */ + + /* Clear out old sampass info. */ + TALLOC_FREE(sampass); + acct_ctrl = 0; + username = NULL; + nt_pw = NULL; + lm_pw = NULL; + + sampass = samu_new(mem_ctx); + if (sampass == NULL) { + return NT_STATUS_NO_MEMORY; + } + + mutex_name_by_user = talloc_asprintf(mem_ctx, + "check_sam_security_mutex_%s", + user_info->mapped.account_name); + if (mutex_name_by_user == NULL) { + nt_status = NT_STATUS_NO_MEMORY; + goto done; + } + + /* Grab the named mutex under root with 30 second timeout. */ + become_root(); + mtx = grab_named_mutex(mem_ctx, mutex_name_by_user, 30); + if (mtx != NULL) { + /* Re-load the account information if we got the mutex. */ + ret = pdb_getsampwnam(sampass, user_info->mapped.account_name); + } + unbecome_root(); + + /* Everything from here on until mtx is freed is done under the mutex.*/ + + if (mtx == NULL) { + DBG_ERR("Acquisition of mutex %s failed " + "for user %s\n", + mutex_name_by_user, + user_info->mapped.account_name); + nt_status = NT_STATUS_INTERNAL_ERROR; + goto done; + } + + if (!ret) { + /* + * Re-load of account failed. This could only happen if the + * user was deleted in the meantime. + */ + DBG_NOTICE("reload of user '%s' in passdb failed.\n", + user_info->mapped.account_name); + nt_status = NT_STATUS_NO_SUCH_USER; + goto done; + } + + /* Re-load the account control info. */ + acct_ctrl = pdb_get_acct_ctrl(sampass); + username = pdb_get_username(sampass); + + /* + * Check if the account is now locked out - now under the mutex. + * This can happen if the server is under + * a password guess attack and the ACB_AUTOLOCK is set by + * another process. + */ + if (acct_ctrl & ACB_AUTOLOCK) { + DBG_NOTICE("Account for user %s was locked out.\n", username); + nt_status = NT_STATUS_ACCOUNT_LOCKED_OUT; + goto done; + } + /* Notify passdb backend of login success/failure. If not NT_STATUS_OK the backend doesn't like the login */ @@ -510,6 +585,8 @@ done: TALLOC_FREE(sampass); data_blob_free(&user_sess_key); data_blob_free(&lm_sess_key); + TALLOC_FREE(mutex_name_by_user); + TALLOC_FREE(mtx); return nt_status; } -- 2.27.0