mirror of
https://github.com/samba-team/samba.git
synced 2024-12-22 13:34:15 +03:00
CVE-2021-20251 s3: Ensure bad password count atomic updates for SAMR password change
The bad password count is supposed to limit the number of failed login attempt a user can make before being temporarily locked out, but race conditions between processes have allowed determined attackers to make many more than the specified number of attempts. This is especially bad on constrained or overcommitted hardware. To fix this, once a bad password is detected, we reload the sam account information under a user-specific mutex, ensuring we have an up to date bad password count. Derived from a similar patch to source3/auth/check_samsec.c by Jeremy Allison <jra@samba.org> BUG: https://bugzilla.samba.org/show_bug.cgi?id=14611 Signed-off-by: Joseph Sutton <josephsutton@catalyst.net.nz> Reviewed-by: Andreas Schneider <asn@samba.org> Reviewed-by: Andrew Bartlett <abartlet@samba.org>
This commit is contained in:
parent
8587734bf9
commit
65c473d4a5
@ -1205,6 +1205,8 @@ NTSTATUS pass_oem_change(char *user, const char *rhost,
|
||||
bool ret = false;
|
||||
bool updated_badpw = false;
|
||||
NTSTATUS update_login_attempts_status;
|
||||
char *mutex_name_by_user = NULL;
|
||||
struct named_mutex *mtx = NULL;
|
||||
|
||||
if (!(sampass = samu_new(NULL))) {
|
||||
return NT_STATUS_NO_MEMORY;
|
||||
@ -1216,15 +1218,15 @@ NTSTATUS pass_oem_change(char *user, const char *rhost,
|
||||
|
||||
if (ret == false) {
|
||||
DEBUG(0,("pass_oem_change: getsmbpwnam returned NULL\n"));
|
||||
TALLOC_FREE(sampass);
|
||||
return NT_STATUS_NO_SUCH_USER;
|
||||
nt_status = NT_STATUS_NO_SUCH_USER;
|
||||
goto done;
|
||||
}
|
||||
|
||||
/* Quit if the account was locked out. */
|
||||
if (pdb_get_acct_ctrl(sampass) & ACB_AUTOLOCK) {
|
||||
DEBUG(3,("check_sam_security: Account for user %s was locked out.\n", user));
|
||||
TALLOC_FREE(sampass);
|
||||
return NT_STATUS_ACCOUNT_LOCKED_OUT;
|
||||
nt_status = NT_STATUS_ACCOUNT_LOCKED_OUT;
|
||||
goto done;
|
||||
}
|
||||
|
||||
nt_status = check_oem_password(user,
|
||||
@ -1235,6 +1237,71 @@ NTSTATUS pass_oem_change(char *user, const char *rhost,
|
||||
sampass,
|
||||
&new_passwd);
|
||||
|
||||
/*
|
||||
* 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);
|
||||
|
||||
sampass = samu_new(NULL);
|
||||
if (sampass == NULL) {
|
||||
return NT_STATUS_NO_MEMORY;
|
||||
}
|
||||
|
||||
mutex_name_by_user = talloc_asprintf(NULL,
|
||||
"check_sam_security_mutex_%s",
|
||||
user);
|
||||
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(NULL, mutex_name_by_user, 30);
|
||||
if (mtx != NULL) {
|
||||
/* Re-load the account information if we got the mutex. */
|
||||
ret = pdb_getsampwnam(sampass, user);
|
||||
}
|
||||
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);
|
||||
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);
|
||||
nt_status = NT_STATUS_NO_SUCH_USER;
|
||||
goto done;
|
||||
}
|
||||
|
||||
/*
|
||||
* 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 (pdb_get_acct_ctrl(sampass) & ACB_AUTOLOCK) {
|
||||
DBG_NOTICE("Account for user %s was locked out.\n", user);
|
||||
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
|
||||
@ -1282,8 +1349,7 @@ NTSTATUS pass_oem_change(char *user, const char *rhost,
|
||||
}
|
||||
|
||||
if (!NT_STATUS_IS_OK(nt_status)) {
|
||||
TALLOC_FREE(sampass);
|
||||
return nt_status;
|
||||
goto done;
|
||||
}
|
||||
|
||||
/* We've already checked the old password here.... */
|
||||
@ -1294,7 +1360,10 @@ NTSTATUS pass_oem_change(char *user, const char *rhost,
|
||||
|
||||
memset(new_passwd, 0, strlen(new_passwd));
|
||||
|
||||
done:
|
||||
TALLOC_FREE(sampass);
|
||||
TALLOC_FREE(mutex_name_by_user);
|
||||
TALLOC_FREE(mtx);
|
||||
|
||||
return nt_status;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user