mirror of
https://github.com/TalAloni/SMBLibrary.git
synced 2025-07-24 02:00:27 +02:00
IndependentNTLMAuthenticationProvider: Added account lockout mechanism to hinder bruteforce attacks
This commit is contained in:
parent
ddafceb45c
commit
a32e62d020
3 changed files with 108 additions and 3 deletions
77
SMBLibrary/Authentication/LoginCounter.cs
Normal file
77
SMBLibrary/Authentication/LoginCounter.cs
Normal file
|
@ -0,0 +1,77 @@
|
|||
/* Copyright (C) 2017 Tal Aloni <tal.aloni.il@gmail.com>. All rights reserved.
|
||||
*
|
||||
* You can redistribute this program and/or modify it under the terms of
|
||||
* the GNU Lesser Public License as published by the Free Software Foundation,
|
||||
* either version 3 of the License, or (at your option) any later version.
|
||||
*/
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace SMBLibrary.Authentication
|
||||
{
|
||||
public class LoginCounter
|
||||
{
|
||||
public class LoginEntry
|
||||
{
|
||||
public DateTime LoginWindowStartDT;
|
||||
public int NumberOfAttempts;
|
||||
}
|
||||
|
||||
private int m_maxLoginAttemptsInWindow;
|
||||
private TimeSpan m_loginWindowDuration;
|
||||
private Dictionary<string, LoginEntry> m_loginEntries = new Dictionary<string, LoginEntry>();
|
||||
|
||||
public LoginCounter(int maxLoginAttemptsInWindow, TimeSpan loginWindowDuration)
|
||||
{
|
||||
m_maxLoginAttemptsInWindow = maxLoginAttemptsInWindow;
|
||||
m_loginWindowDuration = loginWindowDuration;
|
||||
}
|
||||
|
||||
public bool HasRemainingLoginAttempts(string userID)
|
||||
{
|
||||
return HasRemainingLoginAttempts(userID, false);
|
||||
}
|
||||
|
||||
public bool HasRemainingLoginAttempts(string userID, bool incrementCount)
|
||||
{
|
||||
lock (m_loginEntries)
|
||||
{
|
||||
LoginEntry entry;
|
||||
if (m_loginEntries.TryGetValue(userID, out entry))
|
||||
{
|
||||
if (entry.LoginWindowStartDT.Add(m_loginWindowDuration) >= DateTime.Now)
|
||||
{
|
||||
// Existing login Window
|
||||
if (incrementCount)
|
||||
{
|
||||
entry.NumberOfAttempts++;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// New login Window
|
||||
if (!incrementCount)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
entry.LoginWindowStartDT = DateTime.Now;
|
||||
entry.NumberOfAttempts = 1;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!incrementCount)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
entry = new LoginEntry();
|
||||
entry.LoginWindowStartDT = DateTime.Now;
|
||||
entry.NumberOfAttempts = 1;
|
||||
m_loginEntries.Add(userID, entry);
|
||||
}
|
||||
return (entry.NumberOfAttempts < m_maxLoginAttemptsInWindow);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -33,14 +33,22 @@ namespace SMBLibrary.Authentication.NTLM
|
|||
}
|
||||
}
|
||||
|
||||
private static readonly int DefaultMaxLoginAttemptsInWindow = 12;
|
||||
private static readonly TimeSpan DefaultLoginWindowDuration = new TimeSpan(0, 5, 0);
|
||||
private GetUserPassword m_GetUserPassword;
|
||||
private LoginCounter m_loginCounter;
|
||||
|
||||
/// <param name="getUserPassword">
|
||||
/// The NTLM challenge response will be compared against the provided password.
|
||||
/// </param>
|
||||
public IndependentNTLMAuthenticationProvider(GetUserPassword getUserPassword)
|
||||
public IndependentNTLMAuthenticationProvider(GetUserPassword getUserPassword) : this(getUserPassword, DefaultMaxLoginAttemptsInWindow, DefaultLoginWindowDuration)
|
||||
{
|
||||
}
|
||||
|
||||
public IndependentNTLMAuthenticationProvider(GetUserPassword getUserPassword, int maxLoginAttemptsInWindow, TimeSpan loginWindowDuration)
|
||||
{
|
||||
m_GetUserPassword = getUserPassword;
|
||||
m_loginCounter = new LoginCounter(maxLoginAttemptsInWindow, loginWindowDuration);
|
||||
}
|
||||
|
||||
public override NTStatus GetChallengeMessage(out object context, NegotiateMessage negotiateMessage, out ChallengeMessage challengeMessage)
|
||||
|
@ -154,6 +162,11 @@ namespace SMBLibrary.Authentication.NTLM
|
|||
}
|
||||
}
|
||||
|
||||
if (!m_loginCounter.HasRemainingLoginAttempts(message.UserName.ToLower()))
|
||||
{
|
||||
return NTStatus.STATUS_ACCOUNT_LOCKED_OUT;
|
||||
}
|
||||
|
||||
string password = m_GetUserPassword(message.UserName);
|
||||
if (password == null)
|
||||
{
|
||||
|
@ -164,7 +177,14 @@ namespace SMBLibrary.Authentication.NTLM
|
|||
}
|
||||
else
|
||||
{
|
||||
return NTStatus.STATUS_LOGON_FAILURE;
|
||||
if (m_loginCounter.HasRemainingLoginAttempts(message.UserName.ToLower(), true))
|
||||
{
|
||||
return NTStatus.STATUS_LOGON_FAILURE;
|
||||
}
|
||||
else
|
||||
{
|
||||
return NTStatus.STATUS_ACCOUNT_LOCKED_OUT;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -228,7 +248,14 @@ namespace SMBLibrary.Authentication.NTLM
|
|||
}
|
||||
else
|
||||
{
|
||||
return NTStatus.STATUS_LOGON_FAILURE;
|
||||
if (m_loginCounter.HasRemainingLoginAttempts(message.UserName.ToLower(), true))
|
||||
{
|
||||
return NTStatus.STATUS_LOGON_FAILURE;
|
||||
}
|
||||
else
|
||||
{
|
||||
return NTStatus.STATUS_ACCOUNT_LOCKED_OUT;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -38,6 +38,7 @@
|
|||
<Compile Include="Authentication\GSSAPI\SPNEGO\SimpleProtectedNegotiationToken.cs" />
|
||||
<Compile Include="Authentication\GSSAPI\SPNEGO\SimpleProtectedNegotiationTokenInit.cs" />
|
||||
<Compile Include="Authentication\GSSAPI\SPNEGO\SimpleProtectedNegotiationTokenResponse.cs" />
|
||||
<Compile Include="Authentication\LoginCounter.cs" />
|
||||
<Compile Include="Authentication\NTLM\Helpers\AuthenticationMessageUtils.cs" />
|
||||
<Compile Include="Authentication\NTLM\Helpers\AVPairUtils.cs" />
|
||||
<Compile Include="Authentication\NTLM\Helpers\MD4.cs" />
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue