mirror of
https://github.com/TalAloni/SMBLibrary.git
synced 2025-08-04 23:11:29 +02:00
Improved NTLM authentication API and implementation
This commit is contained in:
parent
6e52e002a6
commit
345f4ae444
7 changed files with 125 additions and 177 deletions
|
@ -34,7 +34,8 @@ namespace SMBLibrary.Authentication
|
|||
|
||||
/// <summary>
|
||||
/// NegotiateLanManagerKey and NegotiateExtendedSecurity are mutually exclusive
|
||||
/// If both are set then NegotiateLanManagerKey must be ignored
|
||||
/// If both are set then NegotiateLanManagerKey must be ignored.
|
||||
/// NTLM v2 requires this flag to be set.
|
||||
/// </summary>
|
||||
NegotiateExtendedSecurity = 0x80000, // NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY
|
||||
NegotiateIdentify = 0x100000, // NTLMSSP_NEGOTIATE_IDENTIFY
|
||||
|
|
|
@ -13,13 +13,9 @@ namespace SMBLibrary.Server
|
|||
{
|
||||
public interface INTLMAuthenticationProvider
|
||||
{
|
||||
// CIFS style NTLM
|
||||
byte[] GenerateServerChallenge();
|
||||
User Authenticate(string accountNameToAuth, byte[] lmResponse, byte[] ntlmResponse);
|
||||
|
||||
// SSPI style NTLM
|
||||
byte[] GetChallengeMessageBytes(byte[] negotiateMessageBytes);
|
||||
User Authenticate(byte[] authenticateMessageBytes);
|
||||
ChallengeMessage GetChallengeMessage(NegotiateMessage negotiateMessage);
|
||||
|
||||
bool Authenticate(AuthenticateMessage authenticateMessage);
|
||||
|
||||
/// <summary>
|
||||
/// Permit access to this user via the guest user account if the normal authentication process fails.
|
||||
|
|
|
@ -28,7 +28,7 @@ namespace SMBLibrary.Server
|
|||
/// <summary>
|
||||
/// LM v1 / NTLM v1
|
||||
/// </summary>
|
||||
public User AuthenticateV1(string accountNameToAuth, byte[] serverChallenge, byte[] lmResponse, byte[] ntlmResponse)
|
||||
private User AuthenticateV1(string accountNameToAuth, byte[] serverChallenge, byte[] lmResponse, byte[] ntlmResponse)
|
||||
{
|
||||
for (int index = 0; index < this.Count; index++)
|
||||
{
|
||||
|
@ -56,7 +56,7 @@ namespace SMBLibrary.Server
|
|||
/// <summary>
|
||||
/// LM v1 / NTLM v1 Extended Security
|
||||
/// </summary>
|
||||
public User AuthenticateV1Extended(string accountNameToAuth, byte[] serverChallenge, byte[] lmResponse, byte[] ntlmResponse)
|
||||
private User AuthenticateV1Extended(string accountNameToAuth, byte[] serverChallenge, byte[] lmResponse, byte[] ntlmResponse)
|
||||
{
|
||||
for (int index = 0; index < this.Count; index++)
|
||||
{
|
||||
|
@ -80,7 +80,7 @@ namespace SMBLibrary.Server
|
|||
/// <summary>
|
||||
/// LM v2 / NTLM v2
|
||||
/// </summary>
|
||||
public User AuthenticateV2(string domainNameToAuth, string accountNameToAuth, byte[] serverChallenge, byte[] lmResponse, byte[] ntlmResponse)
|
||||
private User AuthenticateV2(string domainNameToAuth, string accountNameToAuth, byte[] serverChallenge, byte[] lmResponse, byte[] ntlmResponse)
|
||||
{
|
||||
for (int index = 0; index < this.Count; index++)
|
||||
{
|
||||
|
@ -112,13 +112,13 @@ namespace SMBLibrary.Server
|
|||
return null;
|
||||
}
|
||||
|
||||
public byte[] GenerateServerChallenge()
|
||||
private byte[] GenerateServerChallenge()
|
||||
{
|
||||
new Random().NextBytes(m_serverChallenge);
|
||||
return m_serverChallenge;
|
||||
}
|
||||
|
||||
public ChallengeMessage GetChallengeMessage(byte[] negotiateMessageBytes)
|
||||
public ChallengeMessage GetChallengeMessage(NegotiateMessage negotiateMessage)
|
||||
{
|
||||
byte[] serverChallenge = GenerateServerChallenge();
|
||||
|
||||
|
@ -138,42 +138,29 @@ namespace SMBLibrary.Server
|
|||
return message;
|
||||
}
|
||||
|
||||
public byte[] GetChallengeMessageBytes(byte[] negotiateMessageBytes)
|
||||
public bool Authenticate(AuthenticateMessage message)
|
||||
{
|
||||
ChallengeMessage message = GetChallengeMessage(negotiateMessageBytes);
|
||||
return message.GetBytes();
|
||||
}
|
||||
if ((message.NegotiateFlags & NegotiateFlags.NegotiateAnonymous) > 0)
|
||||
{
|
||||
return this.EnableGuestLogin;
|
||||
}
|
||||
|
||||
public User Authenticate(byte[] authenticateMessageBytes)
|
||||
{
|
||||
AuthenticateMessage message = new AuthenticateMessage(authenticateMessageBytes);
|
||||
return Authenticate(message);
|
||||
}
|
||||
|
||||
public User Authenticate(AuthenticateMessage message)
|
||||
{
|
||||
User user;
|
||||
if ((message.NegotiateFlags & NegotiateFlags.NegotiateExtendedSecurity) > 0)
|
||||
{
|
||||
user = AuthenticateV1Extended(message.UserName, m_serverChallenge, message.LmChallengeResponse, message.NtChallengeResponse);
|
||||
if (user == null)
|
||||
{
|
||||
// NTLM v2:
|
||||
user = AuthenticateV2(message.DomainName, message.UserName, m_serverChallenge, message.LmChallengeResponse, message.NtChallengeResponse);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
user = AuthenticateV1(message.UserName, m_serverChallenge, message.LmChallengeResponse, message.NtChallengeResponse);
|
||||
}
|
||||
|
||||
if (user == null)
|
||||
{
|
||||
// NTLM v2
|
||||
user = AuthenticateV2(message.DomainName, message.UserName, m_serverChallenge, message.LmChallengeResponse, message.NtChallengeResponse);
|
||||
}
|
||||
|
||||
return user;
|
||||
}
|
||||
|
||||
public User Authenticate(string accountNameToAuth, byte[] lmResponse, byte[] ntlmResponse)
|
||||
{
|
||||
return AuthenticateV1(accountNameToAuth, m_serverChallenge, lmResponse, ntlmResponse);
|
||||
return (user != null);
|
||||
}
|
||||
|
||||
public bool FallbackToGuest(string userName)
|
||||
|
|
|
@ -18,7 +18,7 @@ namespace SMBLibrary.Server.SMB1
|
|||
/// </summary>
|
||||
public class NegotiateHelper
|
||||
{
|
||||
internal static NegotiateResponseNTLM GetNegotiateResponse(SMB1Header header, NegotiateRequest request, byte[] serverChallenge)
|
||||
internal static NegotiateResponseNTLM GetNegotiateResponse(SMB1Header header, NegotiateRequest request, INTLMAuthenticationProvider users)
|
||||
{
|
||||
NegotiateResponseNTLM response = new NegotiateResponseNTLM();
|
||||
|
||||
|
@ -37,7 +37,8 @@ namespace SMBLibrary.Server.SMB1
|
|||
ServerCapabilities.LargeWrite;
|
||||
response.SystemTime = DateTime.UtcNow;
|
||||
response.ServerTimeZone = (short)-TimeZone.CurrentTimeZone.GetUtcOffset(DateTime.Now).TotalMinutes;
|
||||
response.Challenge = serverChallenge;
|
||||
ChallengeMessage challengeMessage = users.GetChallengeMessage(CreateNegotiateMessage());
|
||||
response.Challenge = challengeMessage.ServerChallenge;
|
||||
response.DomainName = String.Empty;
|
||||
response.ServerName = String.Empty;
|
||||
|
||||
|
@ -67,5 +68,13 @@ namespace SMBLibrary.Server.SMB1
|
|||
|
||||
return response;
|
||||
}
|
||||
|
||||
private static NegotiateMessage CreateNegotiateMessage()
|
||||
{
|
||||
NegotiateMessage negotiateMessage = new NegotiateMessage();
|
||||
negotiateMessage.NegotiateFlags = NegotiateFlags.NegotiateUnicode | NegotiateFlags.NegotiateOEM | NegotiateFlags.RequestTarget | NegotiateFlags.NegotiateSign | NegotiateFlags.NegotiateSeal | NegotiateFlags.NegotiateLanManagerKey | NegotiateFlags.NegotiateNTLMKey | NegotiateFlags.NegotiateAlwaysSign | NegotiateFlags.NegotiateVersion | NegotiateFlags.Negotiate128 | NegotiateFlags.Negotiate56;
|
||||
negotiateMessage.Version = Authentication.Version.Server2003;
|
||||
return negotiateMessage;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -24,10 +24,11 @@ namespace SMBLibrary.Server.SMB1
|
|||
// The PrimaryDomain field in the request is used to determine with domain controller should authenticate the user credentials,
|
||||
// However, the domain controller itself does not use this field.
|
||||
// See: http://msdn.microsoft.com/en-us/library/windows/desktop/aa378749%28v=vs.85%29.aspx
|
||||
User user;
|
||||
AuthenticateMessage message = CreateAuthenticateMessage(request.AccountName, request.OEMPassword, request.UnicodePassword);
|
||||
bool loginSuccess;
|
||||
try
|
||||
{
|
||||
user = users.Authenticate(request.AccountName, request.OEMPassword, request.UnicodePassword);
|
||||
loginSuccess = users.Authenticate(message);
|
||||
}
|
||||
catch (EmptyPasswordNotAllowedException)
|
||||
{
|
||||
|
@ -35,9 +36,9 @@ namespace SMBLibrary.Server.SMB1
|
|||
return new ErrorResponse(CommandName.SMB_COM_SESSION_SETUP_ANDX);
|
||||
}
|
||||
|
||||
if (user != null)
|
||||
if (loginSuccess)
|
||||
{
|
||||
ushort? userID = state.AddConnectedUser(user.AccountName);
|
||||
ushort? userID = state.AddConnectedUser(message.UserName);
|
||||
if (!userID.HasValue)
|
||||
{
|
||||
header.Status = NTStatus.STATUS_TOO_MANY_SESSIONS;
|
||||
|
@ -46,7 +47,7 @@ namespace SMBLibrary.Server.SMB1
|
|||
header.UID = userID.Value;
|
||||
response.PrimaryDomain = request.PrimaryDomain;
|
||||
}
|
||||
else if (users.FallbackToGuest(user.AccountName))
|
||||
else if (users.FallbackToGuest(message.UserName))
|
||||
{
|
||||
ushort? userID = state.AddConnectedUser("Guest");
|
||||
if (!userID.HasValue)
|
||||
|
@ -98,23 +99,25 @@ namespace SMBLibrary.Server.SMB1
|
|||
MessageTypeName messageType = AuthenticationMessageUtils.GetMessageType(messageBytes);
|
||||
if (messageType == MessageTypeName.Negotiate)
|
||||
{
|
||||
byte[] challengeMessageBytes = users.GetChallengeMessageBytes(messageBytes);
|
||||
NegotiateMessage negotiateMessage = new NegotiateMessage(messageBytes);
|
||||
ChallengeMessage challengeMessage = users.GetChallengeMessage(negotiateMessage);
|
||||
if (isRawMessage)
|
||||
{
|
||||
response.SecurityBlob = challengeMessageBytes;
|
||||
response.SecurityBlob = challengeMessage.GetBytes();
|
||||
}
|
||||
else
|
||||
{
|
||||
response.SecurityBlob = GSSAPIHelper.GetGSSTokenResponseBytesFromNTLMSSPMessage(challengeMessageBytes);
|
||||
response.SecurityBlob = GSSAPIHelper.GetGSSTokenResponseBytesFromNTLMSSPMessage(challengeMessage.GetBytes());
|
||||
}
|
||||
header.Status = NTStatus.STATUS_MORE_PROCESSING_REQUIRED;
|
||||
}
|
||||
else // MessageTypeName.Authenticate
|
||||
{
|
||||
User user;
|
||||
AuthenticateMessage authenticateMessage = new AuthenticateMessage(messageBytes);
|
||||
bool loginSuccess;
|
||||
try
|
||||
{
|
||||
user = users.Authenticate(messageBytes);
|
||||
loginSuccess = users.Authenticate(authenticateMessage);
|
||||
}
|
||||
catch (EmptyPasswordNotAllowedException)
|
||||
{
|
||||
|
@ -122,9 +125,9 @@ namespace SMBLibrary.Server.SMB1
|
|||
return new ErrorResponse(CommandName.SMB_COM_SESSION_SETUP_ANDX);
|
||||
}
|
||||
|
||||
if (user != null)
|
||||
if (loginSuccess)
|
||||
{
|
||||
ushort? userID = state.AddConnectedUser(user.AccountName);
|
||||
ushort? userID = state.AddConnectedUser(authenticateMessage.UserName);
|
||||
if (!userID.HasValue)
|
||||
{
|
||||
header.Status = NTStatus.STATUS_TOO_MANY_SESSIONS;
|
||||
|
@ -132,7 +135,7 @@ namespace SMBLibrary.Server.SMB1
|
|||
}
|
||||
header.UID = userID.Value;
|
||||
}
|
||||
else if (users.FallbackToGuest(user.AccountName))
|
||||
else if (users.FallbackToGuest(authenticateMessage.UserName))
|
||||
{
|
||||
ushort? userID = state.AddConnectedUser("Guest");
|
||||
if (!userID.HasValue)
|
||||
|
@ -154,5 +157,16 @@ namespace SMBLibrary.Server.SMB1
|
|||
|
||||
return response;
|
||||
}
|
||||
|
||||
private static AuthenticateMessage CreateAuthenticateMessage(string accountNameToAuth, byte[] lmResponse, byte[] ntlmResponse)
|
||||
{
|
||||
AuthenticateMessage authenticateMessage = new AuthenticateMessage();
|
||||
authenticateMessage.NegotiateFlags = NegotiateFlags.NegotiateUnicode | NegotiateFlags.NegotiateOEM | NegotiateFlags.RequestTarget | NegotiateFlags.NegotiateSign | NegotiateFlags.NegotiateSeal | NegotiateFlags.NegotiateLanManagerKey | NegotiateFlags.NegotiateNTLMKey | NegotiateFlags.NegotiateAlwaysSign | NegotiateFlags.NegotiateVersion | NegotiateFlags.Negotiate128 | NegotiateFlags.Negotiate56;
|
||||
authenticateMessage.UserName = accountNameToAuth;
|
||||
authenticateMessage.LmChallengeResponse = lmResponse;
|
||||
authenticateMessage.NtChallengeResponse = ntlmResponse;
|
||||
authenticateMessage.Version = Authentication.Version.Server2003;
|
||||
return authenticateMessage;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -64,8 +64,7 @@ namespace SMBLibrary.Server
|
|||
}
|
||||
else
|
||||
{
|
||||
byte[] serverChallenge = m_users.GenerateServerChallenge();
|
||||
return NegotiateHelper.GetNegotiateResponse(header, request, serverChallenge);
|
||||
return NegotiateHelper.GetNegotiateResponse(header, request, m_users);
|
||||
}
|
||||
}
|
||||
else
|
||||
|
|
|
@ -29,158 +29,97 @@ namespace SMBLibrary.Server.Win32
|
|||
}
|
||||
}
|
||||
|
||||
public byte[] GenerateServerChallenge()
|
||||
public ChallengeMessage GetChallengeMessage(NegotiateMessage negotiateMessage)
|
||||
{
|
||||
NegotiateMessage negotiateMessage = new NegotiateMessage();
|
||||
negotiateMessage.NegotiateFlags = NegotiateFlags.NegotiateUnicode | NegotiateFlags.NegotiateOEM | NegotiateFlags.RequestTarget | NegotiateFlags.NegotiateSign | NegotiateFlags.NegotiateSeal | NegotiateFlags.NegotiateLanManagerKey | NegotiateFlags.NegotiateNTLMKey | NegotiateFlags.NegotiateAlwaysSign | NegotiateFlags.NegotiateVersion | NegotiateFlags.Negotiate128 | NegotiateFlags.Negotiate56;
|
||||
negotiateMessage.Version = Authentication.Version.Server2003;
|
||||
|
||||
byte[] negotiateMessageBytes = negotiateMessage.GetBytes();
|
||||
byte[] challengeMessageBytes = SSPIHelper.GetType2Message(negotiateMessageBytes, out m_serverContext);
|
||||
ChallengeMessage challengeMessage = new ChallengeMessage(challengeMessageBytes);
|
||||
|
||||
m_serverChallenge = challengeMessage.ServerChallenge;
|
||||
return m_serverChallenge;
|
||||
}
|
||||
|
||||
public byte[] GetChallengeMessageBytes(byte[] negotiateMessageBytes)
|
||||
{
|
||||
byte[] challengeMessageBytes = SSPIHelper.GetType2Message(negotiateMessageBytes, out m_serverContext);
|
||||
ChallengeMessage message = new ChallengeMessage(challengeMessageBytes);
|
||||
m_serverChallenge = message.ServerChallenge;
|
||||
return challengeMessageBytes;
|
||||
return challengeMessage;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Note: The 'limitblankpassworduse' (Under HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Lsa)
|
||||
/// will cause AcceptSecurityContext to return SEC_E_LOGON_DENIED when the correct password is blank.
|
||||
/// Authenticate will return false when the password is correct in these cases:
|
||||
/// 1. The correct password is blank and 'limitblankpassworduse' is set to 1.
|
||||
/// 2. The user is listed in the "Deny access to this computer from the network" list.
|
||||
/// </summary>
|
||||
public User Authenticate(string accountNameToAuth, byte[] lmResponse, byte[] ntlmResponse)
|
||||
public bool Authenticate(AuthenticateMessage message)
|
||||
{
|
||||
if (accountNameToAuth == String.Empty ||
|
||||
(String.Equals(accountNameToAuth, "Guest", StringComparison.InvariantCultureIgnoreCase) && IsPasswordEmpty(lmResponse, ntlmResponse) && this.EnableGuestLogin))
|
||||
if ((message.NegotiateFlags & NegotiateFlags.NegotiateAnonymous) > 0)
|
||||
{
|
||||
int guestIndex = IndexOf("Guest");
|
||||
if (guestIndex >= 0)
|
||||
{
|
||||
return this[guestIndex];
|
||||
}
|
||||
return null;
|
||||
return this.EnableGuestLogin;
|
||||
}
|
||||
|
||||
int index = IndexOf(accountNameToAuth);
|
||||
if (index >= 0)
|
||||
// AuthenticateType3Message is not reliable when 'limitblankpassworduse' is set to 1 and the user has an empty password set.
|
||||
// Note: Windows LogonUser API calls will be listed in the security event log.
|
||||
if (!AreEmptyPasswordsAllowed() &&
|
||||
IsPasswordEmpty(message) &&
|
||||
LoginAPI.HasEmptyPassword(message.UserName))
|
||||
{
|
||||
// We should not spam the security event log, and should call the Windows LogonUser API
|
||||
// just to verify the user has a blank password.
|
||||
if (!AreEmptyPasswordsAllowed() &&
|
||||
IsPasswordEmpty(lmResponse, ntlmResponse) &&
|
||||
LoginAPI.HasEmptyPassword(accountNameToAuth))
|
||||
if (FallbackToGuest(message.UserName))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new EmptyPasswordNotAllowedException();
|
||||
}
|
||||
|
||||
AuthenticateMessage authenticateMessage = new AuthenticateMessage();
|
||||
authenticateMessage.NegotiateFlags = NegotiateFlags.NegotiateUnicode | NegotiateFlags.NegotiateOEM | NegotiateFlags.RequestTarget | NegotiateFlags.NegotiateSign | NegotiateFlags.NegotiateSeal | NegotiateFlags.NegotiateLanManagerKey | NegotiateFlags.NegotiateNTLMKey | NegotiateFlags.NegotiateAlwaysSign | NegotiateFlags.NegotiateVersion | NegotiateFlags.Negotiate128 | NegotiateFlags.Negotiate56;
|
||||
authenticateMessage.UserName = accountNameToAuth;
|
||||
authenticateMessage.LmChallengeResponse = lmResponse;
|
||||
authenticateMessage.NtChallengeResponse = ntlmResponse;
|
||||
authenticateMessage.Version = Authentication.Version.Server2003;
|
||||
byte[] authenticateMessageBytes = authenticateMessage.GetBytes();
|
||||
|
||||
bool success = SSPIHelper.AuthenticateType3Message(m_serverContext, authenticateMessageBytes);
|
||||
if (success)
|
||||
{
|
||||
return this[index];
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Note: The 'limitblankpassworduse' (Under HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Lsa)
|
||||
/// will cause AcceptSecurityContext to return SEC_E_LOGON_DENIED when the correct password is blank.
|
||||
/// </summary>
|
||||
public User Authenticate(byte[] authenticateMessageBytes)
|
||||
{
|
||||
AuthenticateMessage message = new AuthenticateMessage(authenticateMessageBytes);
|
||||
if ((message.NegotiateFlags & NegotiateFlags.NegotiateAnonymous) > 0 ||
|
||||
(String.Equals(message.UserName, "Guest", StringComparison.InvariantCultureIgnoreCase) && IsPasswordEmpty(message) && this.EnableGuestLogin))
|
||||
{
|
||||
int guestIndex = IndexOf("Guest");
|
||||
if (guestIndex >= 0)
|
||||
{
|
||||
return this[guestIndex];
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
int index = IndexOf(message.UserName);
|
||||
if (index >= 0)
|
||||
{
|
||||
// We should not spam the security event log, and should call the Windows LogonUser API
|
||||
// just to verify the user has a blank password.
|
||||
if (!AreEmptyPasswordsAllowed() &&
|
||||
IsPasswordEmpty(message) &&
|
||||
LoginAPI.HasEmptyPassword(message.UserName))
|
||||
{
|
||||
throw new EmptyPasswordNotAllowedException();
|
||||
}
|
||||
|
||||
bool success = SSPIHelper.AuthenticateType3Message(m_serverContext, authenticateMessageBytes);
|
||||
if (success)
|
||||
{
|
||||
return this[index];
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public bool IsPasswordEmpty(byte[] lmResponse, byte[] ntlmResponse)
|
||||
{
|
||||
// Special case for anonymous authentication
|
||||
// Windows NT4 SP6 will send 1 null byte OEMPassword and 0 bytes UnicodePassword for anonymous authentication
|
||||
if (lmResponse.Length == 0 || ByteUtils.AreByteArraysEqual(lmResponse, new byte[] { 0x00 }) || ntlmResponse.Length == 0)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
byte[] emptyPasswordLMv1Response = NTAuthentication.ComputeLMv1Response(m_serverChallenge, String.Empty);
|
||||
if (ByteUtils.AreByteArraysEqual(emptyPasswordLMv1Response, lmResponse))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
byte[] emptyPasswordNTLMv1Response = NTAuthentication.ComputeNTLMv1Response(m_serverChallenge, String.Empty);
|
||||
if (ByteUtils.AreByteArraysEqual(emptyPasswordNTLMv1Response, ntlmResponse))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
byte[] messageBytes = message.GetBytes();
|
||||
bool success = SSPIHelper.AuthenticateType3Message(m_serverContext, messageBytes);
|
||||
return success;
|
||||
}
|
||||
|
||||
public bool IsPasswordEmpty(AuthenticateMessage message)
|
||||
{
|
||||
// Special case for anonymous authentication, see [MS-NLMP] 3.3.1 - NTLM v1 Authentication
|
||||
// See [MS-NLMP] 3.3.1 - NTLM v1 Authentication
|
||||
// Special case for anonymous authentication:
|
||||
if (message.LmChallengeResponse.Length == 1 || message.NtChallengeResponse.Length == 0)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
byte[] clientChallenge = ByteReader.ReadBytes(message.LmChallengeResponse, 0, 8);
|
||||
byte[] emptyPasswordNTLMv1Response = NTAuthentication.ComputeNTLMv1ExtendedSecurityResponse(m_serverChallenge, clientChallenge, String.Empty);
|
||||
if (ByteUtils.AreByteArraysEqual(emptyPasswordNTLMv1Response, message.NtChallengeResponse))
|
||||
if ((message.NegotiateFlags & NegotiateFlags.NegotiateExtendedSecurity) > 0)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
// NTLM v1 extended security:
|
||||
byte[] clientChallenge = ByteReader.ReadBytes(message.LmChallengeResponse, 0, 8);
|
||||
byte[] emptyPasswordNTLMv1Response = NTAuthentication.ComputeNTLMv1ExtendedSecurityResponse(m_serverChallenge, clientChallenge, String.Empty);
|
||||
if (ByteUtils.AreByteArraysEqual(emptyPasswordNTLMv1Response, message.NtChallengeResponse))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (message.NtChallengeResponse.Length > 24)
|
||||
// NTLM v2:
|
||||
byte[] _LMv2ClientChallenge = ByteReader.ReadBytes(message.LmChallengeResponse, 16, 8);
|
||||
byte[] emptyPasswordLMv2Response = NTAuthentication.ComputeLMv2Response(m_serverChallenge, _LMv2ClientChallenge, String.Empty, message.UserName, message.DomainName);
|
||||
if (ByteUtils.AreByteArraysEqual(emptyPasswordLMv2Response, message.LmChallengeResponse))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (message.NtChallengeResponse.Length > 24)
|
||||
{
|
||||
NTLMv2ClientChallengeStructure clientChallengeStructure = new NTLMv2ClientChallengeStructure(message.NtChallengeResponse, 16);
|
||||
byte[] clientChallengeStructurePadded = clientChallengeStructure.GetBytesPadded();
|
||||
byte[] emptyPasswordNTLMv2Response = NTAuthentication.ComputeNTLMv2Response(m_serverChallenge, clientChallengeStructurePadded, String.Empty, message.UserName, message.DomainName);
|
||||
if (ByteUtils.AreByteArraysEqual(emptyPasswordNTLMv2Response, message.NtChallengeResponse))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
NTLMv2ClientChallengeStructure clientChallengeStructure = new NTLMv2ClientChallengeStructure(message.NtChallengeResponse, 16);
|
||||
byte[] clientChallengeStructurePadded = clientChallengeStructure.GetBytesPadded();
|
||||
byte[] emptyPasswordNTLMv2Response = NTAuthentication.ComputeNTLMv2Response(m_serverChallenge, clientChallengeStructurePadded, String.Empty, message.UserName, message.DomainName);
|
||||
if (ByteUtils.AreByteArraysEqual(emptyPasswordNTLMv2Response, message.NtChallengeResponse))
|
||||
// NTLM v1:
|
||||
byte[] emptyPasswordLMv1Response = NTAuthentication.ComputeLMv1Response(m_serverChallenge, String.Empty);
|
||||
if (ByteUtils.AreByteArraysEqual(emptyPasswordLMv1Response, message.LmChallengeResponse))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
byte[] emptyPasswordNTLMv1Response = NTAuthentication.ComputeNTLMv1Response(m_serverChallenge, String.Empty);
|
||||
if (ByteUtils.AreByteArraysEqual(emptyPasswordNTLMv1Response, message.NtChallengeResponse))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
@ -195,13 +134,16 @@ namespace SMBLibrary.Server.Win32
|
|||
}
|
||||
|
||||
/// <summary>
|
||||
/// We immitate Windows, Guest logins are disabled when the guest account has password set
|
||||
/// We immitate Windows, Guest logins are disabled in any of these cases:
|
||||
/// 1. The Guest account is disabled.
|
||||
/// 2. The Guest account has password set.
|
||||
/// 3. The Guest account is listed in the "deny access to this computer from the network" list.
|
||||
/// </summary>
|
||||
private bool EnableGuestLogin
|
||||
{
|
||||
get
|
||||
{
|
||||
return (IndexOf("Guest") >= 0) && LoginAPI.HasEmptyPassword("Guest");
|
||||
return LoginAPI.ValidateUserPassword("Guest", String.Empty, LogonType.Network);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue