Improved NTLM authentication API and implementation

This commit is contained in:
Tal Aloni 2017-01-13 23:34:37 +02:00
parent 6e52e002a6
commit 345f4ae444
7 changed files with 125 additions and 177 deletions

View file

@ -34,7 +34,8 @@ namespace SMBLibrary.Authentication
/// <summary> /// <summary>
/// NegotiateLanManagerKey and NegotiateExtendedSecurity are mutually exclusive /// 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> /// </summary>
NegotiateExtendedSecurity = 0x80000, // NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY NegotiateExtendedSecurity = 0x80000, // NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY
NegotiateIdentify = 0x100000, // NTLMSSP_NEGOTIATE_IDENTIFY NegotiateIdentify = 0x100000, // NTLMSSP_NEGOTIATE_IDENTIFY

View file

@ -13,13 +13,9 @@ namespace SMBLibrary.Server
{ {
public interface INTLMAuthenticationProvider public interface INTLMAuthenticationProvider
{ {
// CIFS style NTLM ChallengeMessage GetChallengeMessage(NegotiateMessage negotiateMessage);
byte[] GenerateServerChallenge();
User Authenticate(string accountNameToAuth, byte[] lmResponse, byte[] ntlmResponse); bool Authenticate(AuthenticateMessage authenticateMessage);
// SSPI style NTLM
byte[] GetChallengeMessageBytes(byte[] negotiateMessageBytes);
User Authenticate(byte[] authenticateMessageBytes);
/// <summary> /// <summary>
/// Permit access to this user via the guest user account if the normal authentication process fails. /// Permit access to this user via the guest user account if the normal authentication process fails.

View file

@ -28,7 +28,7 @@ namespace SMBLibrary.Server
/// <summary> /// <summary>
/// LM v1 / NTLM v1 /// LM v1 / NTLM v1
/// </summary> /// </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++) for (int index = 0; index < this.Count; index++)
{ {
@ -56,7 +56,7 @@ namespace SMBLibrary.Server
/// <summary> /// <summary>
/// LM v1 / NTLM v1 Extended Security /// LM v1 / NTLM v1 Extended Security
/// </summary> /// </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++) for (int index = 0; index < this.Count; index++)
{ {
@ -80,7 +80,7 @@ namespace SMBLibrary.Server
/// <summary> /// <summary>
/// LM v2 / NTLM v2 /// LM v2 / NTLM v2
/// </summary> /// </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++) for (int index = 0; index < this.Count; index++)
{ {
@ -112,13 +112,13 @@ namespace SMBLibrary.Server
return null; return null;
} }
public byte[] GenerateServerChallenge() private byte[] GenerateServerChallenge()
{ {
new Random().NextBytes(m_serverChallenge); new Random().NextBytes(m_serverChallenge);
return m_serverChallenge; return m_serverChallenge;
} }
public ChallengeMessage GetChallengeMessage(byte[] negotiateMessageBytes) public ChallengeMessage GetChallengeMessage(NegotiateMessage negotiateMessage)
{ {
byte[] serverChallenge = GenerateServerChallenge(); byte[] serverChallenge = GenerateServerChallenge();
@ -138,42 +138,29 @@ namespace SMBLibrary.Server
return message; return message;
} }
public byte[] GetChallengeMessageBytes(byte[] negotiateMessageBytes) public bool Authenticate(AuthenticateMessage message)
{ {
ChallengeMessage message = GetChallengeMessage(negotiateMessageBytes); if ((message.NegotiateFlags & NegotiateFlags.NegotiateAnonymous) > 0)
return message.GetBytes(); {
} return this.EnableGuestLogin;
}
public User Authenticate(byte[] authenticateMessageBytes)
{
AuthenticateMessage message = new AuthenticateMessage(authenticateMessageBytes);
return Authenticate(message);
}
public User Authenticate(AuthenticateMessage message)
{
User user; User user;
if ((message.NegotiateFlags & NegotiateFlags.NegotiateExtendedSecurity) > 0) if ((message.NegotiateFlags & NegotiateFlags.NegotiateExtendedSecurity) > 0)
{ {
user = AuthenticateV1Extended(message.UserName, m_serverChallenge, message.LmChallengeResponse, message.NtChallengeResponse); 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 else
{ {
user = AuthenticateV1(message.UserName, m_serverChallenge, message.LmChallengeResponse, message.NtChallengeResponse); user = AuthenticateV1(message.UserName, m_serverChallenge, message.LmChallengeResponse, message.NtChallengeResponse);
} }
if (user == null) return (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);
} }
public bool FallbackToGuest(string userName) public bool FallbackToGuest(string userName)

View file

@ -18,7 +18,7 @@ namespace SMBLibrary.Server.SMB1
/// </summary> /// </summary>
public class NegotiateHelper 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(); NegotiateResponseNTLM response = new NegotiateResponseNTLM();
@ -37,7 +37,8 @@ namespace SMBLibrary.Server.SMB1
ServerCapabilities.LargeWrite; ServerCapabilities.LargeWrite;
response.SystemTime = DateTime.UtcNow; response.SystemTime = DateTime.UtcNow;
response.ServerTimeZone = (short)-TimeZone.CurrentTimeZone.GetUtcOffset(DateTime.Now).TotalMinutes; 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.DomainName = String.Empty;
response.ServerName = String.Empty; response.ServerName = String.Empty;
@ -67,5 +68,13 @@ namespace SMBLibrary.Server.SMB1
return response; 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;
}
} }
} }

View file

@ -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, // 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. // 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 // 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 try
{ {
user = users.Authenticate(request.AccountName, request.OEMPassword, request.UnicodePassword); loginSuccess = users.Authenticate(message);
} }
catch (EmptyPasswordNotAllowedException) catch (EmptyPasswordNotAllowedException)
{ {
@ -35,9 +36,9 @@ namespace SMBLibrary.Server.SMB1
return new ErrorResponse(CommandName.SMB_COM_SESSION_SETUP_ANDX); 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) if (!userID.HasValue)
{ {
header.Status = NTStatus.STATUS_TOO_MANY_SESSIONS; header.Status = NTStatus.STATUS_TOO_MANY_SESSIONS;
@ -46,7 +47,7 @@ namespace SMBLibrary.Server.SMB1
header.UID = userID.Value; header.UID = userID.Value;
response.PrimaryDomain = request.PrimaryDomain; response.PrimaryDomain = request.PrimaryDomain;
} }
else if (users.FallbackToGuest(user.AccountName)) else if (users.FallbackToGuest(message.UserName))
{ {
ushort? userID = state.AddConnectedUser("Guest"); ushort? userID = state.AddConnectedUser("Guest");
if (!userID.HasValue) if (!userID.HasValue)
@ -98,23 +99,25 @@ namespace SMBLibrary.Server.SMB1
MessageTypeName messageType = AuthenticationMessageUtils.GetMessageType(messageBytes); MessageTypeName messageType = AuthenticationMessageUtils.GetMessageType(messageBytes);
if (messageType == MessageTypeName.Negotiate) if (messageType == MessageTypeName.Negotiate)
{ {
byte[] challengeMessageBytes = users.GetChallengeMessageBytes(messageBytes); NegotiateMessage negotiateMessage = new NegotiateMessage(messageBytes);
ChallengeMessage challengeMessage = users.GetChallengeMessage(negotiateMessage);
if (isRawMessage) if (isRawMessage)
{ {
response.SecurityBlob = challengeMessageBytes; response.SecurityBlob = challengeMessage.GetBytes();
} }
else else
{ {
response.SecurityBlob = GSSAPIHelper.GetGSSTokenResponseBytesFromNTLMSSPMessage(challengeMessageBytes); response.SecurityBlob = GSSAPIHelper.GetGSSTokenResponseBytesFromNTLMSSPMessage(challengeMessage.GetBytes());
} }
header.Status = NTStatus.STATUS_MORE_PROCESSING_REQUIRED; header.Status = NTStatus.STATUS_MORE_PROCESSING_REQUIRED;
} }
else // MessageTypeName.Authenticate else // MessageTypeName.Authenticate
{ {
User user; AuthenticateMessage authenticateMessage = new AuthenticateMessage(messageBytes);
bool loginSuccess;
try try
{ {
user = users.Authenticate(messageBytes); loginSuccess = users.Authenticate(authenticateMessage);
} }
catch (EmptyPasswordNotAllowedException) catch (EmptyPasswordNotAllowedException)
{ {
@ -122,9 +125,9 @@ namespace SMBLibrary.Server.SMB1
return new ErrorResponse(CommandName.SMB_COM_SESSION_SETUP_ANDX); 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) if (!userID.HasValue)
{ {
header.Status = NTStatus.STATUS_TOO_MANY_SESSIONS; header.Status = NTStatus.STATUS_TOO_MANY_SESSIONS;
@ -132,7 +135,7 @@ namespace SMBLibrary.Server.SMB1
} }
header.UID = userID.Value; header.UID = userID.Value;
} }
else if (users.FallbackToGuest(user.AccountName)) else if (users.FallbackToGuest(authenticateMessage.UserName))
{ {
ushort? userID = state.AddConnectedUser("Guest"); ushort? userID = state.AddConnectedUser("Guest");
if (!userID.HasValue) if (!userID.HasValue)
@ -154,5 +157,16 @@ namespace SMBLibrary.Server.SMB1
return response; 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;
}
} }
} }

View file

@ -64,8 +64,7 @@ namespace SMBLibrary.Server
} }
else else
{ {
byte[] serverChallenge = m_users.GenerateServerChallenge(); return NegotiateHelper.GetNegotiateResponse(header, request, m_users);
return NegotiateHelper.GetNegotiateResponse(header, request, serverChallenge);
} }
} }
else else

View file

@ -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[] negotiateMessageBytes = negotiateMessage.GetBytes();
byte[] challengeMessageBytes = SSPIHelper.GetType2Message(negotiateMessageBytes, out m_serverContext); byte[] challengeMessageBytes = SSPIHelper.GetType2Message(negotiateMessageBytes, out m_serverContext);
ChallengeMessage challengeMessage = new ChallengeMessage(challengeMessageBytes); ChallengeMessage challengeMessage = new ChallengeMessage(challengeMessageBytes);
m_serverChallenge = challengeMessage.ServerChallenge; m_serverChallenge = challengeMessage.ServerChallenge;
return m_serverChallenge; return challengeMessage;
}
public byte[] GetChallengeMessageBytes(byte[] negotiateMessageBytes)
{
byte[] challengeMessageBytes = SSPIHelper.GetType2Message(negotiateMessageBytes, out m_serverContext);
ChallengeMessage message = new ChallengeMessage(challengeMessageBytes);
m_serverChallenge = message.ServerChallenge;
return challengeMessageBytes;
} }
/// <summary> /// <summary>
/// Note: The 'limitblankpassworduse' (Under HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Lsa) /// Authenticate will return false when the password is correct in these cases:
/// will cause AcceptSecurityContext to return SEC_E_LOGON_DENIED when the correct password is blank. /// 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> /// </summary>
public User Authenticate(string accountNameToAuth, byte[] lmResponse, byte[] ntlmResponse) public bool Authenticate(AuthenticateMessage message)
{ {
if (accountNameToAuth == String.Empty || if ((message.NegotiateFlags & NegotiateFlags.NegotiateAnonymous) > 0)
(String.Equals(accountNameToAuth, "Guest", StringComparison.InvariantCultureIgnoreCase) && IsPasswordEmpty(lmResponse, ntlmResponse) && this.EnableGuestLogin))
{ {
int guestIndex = IndexOf("Guest"); return this.EnableGuestLogin;
if (guestIndex >= 0)
{
return this[guestIndex];
}
return null;
} }
int index = IndexOf(accountNameToAuth); // AuthenticateType3Message is not reliable when 'limitblankpassworduse' is set to 1 and the user has an empty password set.
if (index >= 0) // 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 if (FallbackToGuest(message.UserName))
// just to verify the user has a blank password. {
if (!AreEmptyPasswordsAllowed() && return false;
IsPasswordEmpty(lmResponse, ntlmResponse) && }
LoginAPI.HasEmptyPassword(accountNameToAuth)) else
{ {
throw new EmptyPasswordNotAllowedException(); 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); byte[] messageBytes = message.GetBytes();
if (index >= 0) bool success = SSPIHelper.AuthenticateType3Message(m_serverContext, messageBytes);
{ return success;
// 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;
} }
public bool IsPasswordEmpty(AuthenticateMessage message) 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) if (message.LmChallengeResponse.Length == 1 || message.NtChallengeResponse.Length == 0)
{ {
return true; return true;
} }
byte[] clientChallenge = ByteReader.ReadBytes(message.LmChallengeResponse, 0, 8); if ((message.NegotiateFlags & NegotiateFlags.NegotiateExtendedSecurity) > 0)
byte[] emptyPasswordNTLMv1Response = NTAuthentication.ComputeNTLMv1ExtendedSecurityResponse(m_serverChallenge, clientChallenge, String.Empty);
if (ByteUtils.AreByteArraysEqual(emptyPasswordNTLMv1Response, message.NtChallengeResponse))
{ {
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); // NTLM v1:
byte[] clientChallengeStructurePadded = clientChallengeStructure.GetBytesPadded(); byte[] emptyPasswordLMv1Response = NTAuthentication.ComputeLMv1Response(m_serverChallenge, String.Empty);
byte[] emptyPasswordNTLMv2Response = NTAuthentication.ComputeNTLMv2Response(m_serverChallenge, clientChallengeStructurePadded, String.Empty, message.UserName, message.DomainName); if (ByteUtils.AreByteArraysEqual(emptyPasswordLMv1Response, message.LmChallengeResponse))
if (ByteUtils.AreByteArraysEqual(emptyPasswordNTLMv2Response, message.NtChallengeResponse)) {
return true;
}
byte[] emptyPasswordNTLMv1Response = NTAuthentication.ComputeNTLMv1Response(m_serverChallenge, String.Empty);
if (ByteUtils.AreByteArraysEqual(emptyPasswordNTLMv1Response, message.NtChallengeResponse))
{ {
return true; return true;
} }
@ -195,13 +134,16 @@ namespace SMBLibrary.Server.Win32
} }
/// <summary> /// <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> /// </summary>
private bool EnableGuestLogin private bool EnableGuestLogin
{ {
get get
{ {
return (IndexOf("Guest") >= 0) && LoginAPI.HasEmptyPassword("Guest"); return LoginAPI.ValidateUserPassword("Guest", String.Empty, LogonType.Network);
} }
} }