Skip to content

Commit

Permalink
CVE-2013-4496:s3-samr: Block attempts to crack passwords via repeated…
Browse files Browse the repository at this point in the history
… password changes

Bug: https://bugzilla.samba.org/show_bug.cgi?id=10245

Signed-off-by: Andrew Bartlett <[email protected]>
Signed-off-by: Stefan Metzmacher <[email protected]>
Signed-off-by: Jeremy Allison <[email protected]>
Reviewed-by: Stefan Metzmacher <[email protected]>
Reviewed-by: Jeremy Allison <[email protected]>
Reviewed-by: Andreas Schneider <[email protected]>
  • Loading branch information
abartlet authored and kseeger committed Mar 11, 2014
1 parent bd9d125 commit 87ad661
Show file tree
Hide file tree
Showing 2 changed files with 129 additions and 16 deletions.
55 changes: 55 additions & 0 deletions source3/rpc_server/samr/srv_samr_chgpasswd.c
Original file line number Diff line number Diff line change
Expand Up @@ -1106,6 +1106,8 @@ NTSTATUS pass_oem_change(char *user, const char *rhost,
struct samu *sampass = NULL;
NTSTATUS nt_status;
bool ret = false;
bool updated_badpw = false;
NTSTATUS update_login_attempts_status;

if (!(sampass = samu_new(NULL))) {
return NT_STATUS_NO_MEMORY;
Expand All @@ -1121,6 +1123,13 @@ NTSTATUS pass_oem_change(char *user, const char *rhost,
return NT_STATUS_NO_SUCH_USER;
}

/* 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 = check_oem_password(user,
password_encrypted_with_lm_hash,
old_lm_hash_encrypted,
Expand All @@ -1129,6 +1138,52 @@ NTSTATUS pass_oem_change(char *user, const char *rhost,
sampass,
&new_passwd);

/*
* Notify passdb backend of login success/failure. If not
* NT_STATUS_OK the backend doesn't like the login
*/
update_login_attempts_status = pdb_update_login_attempts(sampass,
NT_STATUS_IS_OK(nt_status));

if (!NT_STATUS_IS_OK(nt_status)) {
bool increment_bad_pw_count = false;

if (NT_STATUS_EQUAL(nt_status, NT_STATUS_WRONG_PASSWORD) &&
(pdb_get_acct_ctrl(sampass) & ACB_NORMAL) &&
NT_STATUS_IS_OK(update_login_attempts_status))
{
increment_bad_pw_count = true;
}

if (increment_bad_pw_count) {
pdb_increment_bad_password_count(sampass);
updated_badpw = true;
} else {
pdb_update_bad_password_count(sampass,
&updated_badpw);
}
} else {

if ((pdb_get_acct_ctrl(sampass) & ACB_NORMAL) &&
(pdb_get_bad_password_count(sampass) > 0)){
pdb_set_bad_password_count(sampass, 0, PDB_CHANGED);
pdb_set_bad_password_time(sampass, 0, PDB_CHANGED);
updated_badpw = true;
}
}

if (updated_badpw) {
NTSTATUS update_status;
become_root();
update_status = pdb_update_sam_account(sampass);
unbecome_root();

if (!NT_STATUS_IS_OK(update_status)) {
DEBUG(1, ("Failed to modify entry: %s\n",
nt_errstr(update_status)));
}
}

if (!NT_STATUS_IS_OK(nt_status)) {
TALLOC_FREE(sampass);
return nt_status;
Expand Down
90 changes: 74 additions & 16 deletions source3/rpc_server/samr/srv_samr_nt.c
Original file line number Diff line number Diff line change
Expand Up @@ -1715,9 +1715,11 @@ NTSTATUS _samr_ChangePasswordUser(struct pipes_struct *p,
NTSTATUS status;
bool ret = false;
struct samr_user_info *uinfo;
struct samu *pwd;
struct samu *pwd = NULL;
struct samr_Password new_lmPwdHash, new_ntPwdHash, checkHash;
struct samr_Password lm_pwd, nt_pwd;
bool updated_badpw = false;
NTSTATUS update_login_attempts_status;

uinfo = policy_handle_find(p, r->in.user_handle,
SAMR_USER_ACCESS_SET_PASSWORD, NULL,
Expand All @@ -1729,6 +1731,15 @@ NTSTATUS _samr_ChangePasswordUser(struct pipes_struct *p,
DEBUG(5,("_samr_ChangePasswordUser: sid:%s\n",
sid_string_dbg(&uinfo->sid)));

/* basic sanity checking on parameters. Do this before any database ops */
if (!r->in.lm_present || !r->in.nt_present ||
!r->in.old_lm_crypted || !r->in.new_lm_crypted ||
!r->in.old_nt_crypted || !r->in.new_nt_crypted) {
/* we should really handle a change with lm not
present */
return NT_STATUS_INVALID_PARAMETER_MIX;
}

if (!(pwd = samu_new(NULL))) {
return NT_STATUS_NO_MEMORY;
}
Expand All @@ -1742,6 +1753,14 @@ NTSTATUS _samr_ChangePasswordUser(struct pipes_struct *p,
return NT_STATUS_WRONG_PASSWORD;
}

/* Quit if the account was locked out. */
if (pdb_get_acct_ctrl(pwd) & ACB_AUTOLOCK) {
DEBUG(3, ("Account for user %s was locked out.\n",
pdb_get_username(pwd)));
status = NT_STATUS_ACCOUNT_LOCKED_OUT;
goto out;
}

{
const uint8_t *lm_pass, *nt_pass;

Expand All @@ -1750,37 +1769,27 @@ NTSTATUS _samr_ChangePasswordUser(struct pipes_struct *p,

if (!lm_pass || !nt_pass) {
status = NT_STATUS_WRONG_PASSWORD;
goto out;
goto update_login;
}

memcpy(&lm_pwd.hash, lm_pass, sizeof(lm_pwd.hash));
memcpy(&nt_pwd.hash, nt_pass, sizeof(nt_pwd.hash));
}

/* basic sanity checking on parameters. Do this before any database ops */
if (!r->in.lm_present || !r->in.nt_present ||
!r->in.old_lm_crypted || !r->in.new_lm_crypted ||
!r->in.old_nt_crypted || !r->in.new_nt_crypted) {
/* we should really handle a change with lm not
present */
status = NT_STATUS_INVALID_PARAMETER_MIX;
goto out;
}

/* decrypt and check the new lm hash */
D_P16(lm_pwd.hash, r->in.new_lm_crypted->hash, new_lmPwdHash.hash);
D_P16(new_lmPwdHash.hash, r->in.old_lm_crypted->hash, checkHash.hash);
if (memcmp(checkHash.hash, lm_pwd.hash, 16) != 0) {
status = NT_STATUS_WRONG_PASSWORD;
goto out;
goto update_login;
}

/* decrypt and check the new nt hash */
D_P16(nt_pwd.hash, r->in.new_nt_crypted->hash, new_ntPwdHash.hash);
D_P16(new_ntPwdHash.hash, r->in.old_nt_crypted->hash, checkHash.hash);
if (memcmp(checkHash.hash, nt_pwd.hash, 16) != 0) {
status = NT_STATUS_WRONG_PASSWORD;
goto out;
goto update_login;
}

/* The NT Cross is not required by Win2k3 R2, but if present
Expand All @@ -1789,7 +1798,7 @@ NTSTATUS _samr_ChangePasswordUser(struct pipes_struct *p,
D_P16(lm_pwd.hash, r->in.nt_cross->hash, checkHash.hash);
if (memcmp(checkHash.hash, new_ntPwdHash.hash, 16) != 0) {
status = NT_STATUS_WRONG_PASSWORD;
goto out;
goto update_login;
}
}

Expand All @@ -1799,7 +1808,7 @@ NTSTATUS _samr_ChangePasswordUser(struct pipes_struct *p,
D_P16(nt_pwd.hash, r->in.lm_cross->hash, checkHash.hash);
if (memcmp(checkHash.hash, new_lmPwdHash.hash, 16) != 0) {
status = NT_STATUS_WRONG_PASSWORD;
goto out;
goto update_login;
}
}

Expand All @@ -1810,6 +1819,55 @@ NTSTATUS _samr_ChangePasswordUser(struct pipes_struct *p,
}

status = pdb_update_sam_account(pwd);

update_login:

/*
* Notify passdb backend of login success/failure. If not
* NT_STATUS_OK the backend doesn't like the login
*/
update_login_attempts_status = pdb_update_login_attempts(pwd,
NT_STATUS_IS_OK(status));

if (!NT_STATUS_IS_OK(status)) {
bool increment_bad_pw_count = false;

if (NT_STATUS_EQUAL(status,NT_STATUS_WRONG_PASSWORD) &&
(pdb_get_acct_ctrl(pwd) & ACB_NORMAL) &&
NT_STATUS_IS_OK(update_login_attempts_status))
{
increment_bad_pw_count = true;
}

if (increment_bad_pw_count) {
pdb_increment_bad_password_count(pwd);
updated_badpw = true;
} else {
pdb_update_bad_password_count(pwd,
&updated_badpw);
}
} else {

if ((pdb_get_acct_ctrl(pwd) & ACB_NORMAL) &&
(pdb_get_bad_password_count(pwd) > 0)){
pdb_set_bad_password_count(pwd, 0, PDB_CHANGED);
pdb_set_bad_password_time(pwd, 0, PDB_CHANGED);
updated_badpw = true;
}
}

if (updated_badpw) {
NTSTATUS update_status;
become_root();
update_status = pdb_update_sam_account(pwd);
unbecome_root();

if (!NT_STATUS_IS_OK(update_status)) {
DEBUG(1, ("Failed to modify entry: %s\n",
nt_errstr(update_status)));
}
}

out:
TALLOC_FREE(pwd);

Expand Down

0 comments on commit 87ad661

Please sign in to comment.