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;
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,
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
}
if (!NT_STATUS_IS_OK(nt_status)) {
- TALLOC_FREE(sampass);
- return nt_status;
+ goto done;
}
/* We've already checked the old password here.... */
memset(new_passwd, 0, strlen(new_passwd));
+done:
TALLOC_FREE(sampass);
+ TALLOC_FREE(mutex_name_by_user);
+ TALLOC_FREE(mtx);
return nt_status;
}