ソースを参照

Improved NTLM authentication API and implementation

Tal Aloni 8 年 前
コミット
345f4ae444

+ 2 - 1
SMBLibrary/Authentication/AuthenticateMessage/Enums/NegotiateFlags.cs

@@ -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

+ 3 - 7
SMBLibrary/Server/INTLMAuthenticationProvider.cs

@@ -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.

+ 16 - 29
SMBLibrary/Server/IndependentUserCollection.cs

@@ -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();
-        }
-
-        public User Authenticate(byte[] authenticateMessageBytes)
-        {
-            AuthenticateMessage message = new AuthenticateMessage(authenticateMessageBytes);
-            return Authenticate(message);
-        }
+            if ((message.NegotiateFlags & NegotiateFlags.NegotiateAnonymous) > 0)
+            {
+                return this.EnableGuestLogin;
+            }
 
-        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)

+ 11 - 2
SMBLibrary/Server/SMB1/NegotiateHelper.cs

@@ -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;
+        }
     }
 }

+ 27 - 13
SMBLibrary/Server/SMB1/SessionSetupHelper.cs

@@ -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;
+        }
     }
 }

+ 1 - 2
SMBLibrary/Server/SMBServer.SMB1.cs

@@ -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

+ 61 - 119
SMBLibrary/Win32/Win32UserCollection.cs

@@ -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))
                 {
-                    throw new EmptyPasswordNotAllowedException();
+                    return false;
                 }
-
-                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)
+                else
                 {
-                    return this[index];
+                    throw new EmptyPasswordNotAllowedException();
                 }
             }
-            return null;
+
+            byte[] messageBytes = message.GetBytes();
+            bool success = SSPIHelper.AuthenticateType3Message(m_serverContext, messageBytes);
+            return success;
         }
 
-        /// <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)
+        public bool IsPasswordEmpty(AuthenticateMessage message)
         {
-            AuthenticateMessage message = new AuthenticateMessage(authenticateMessageBytes);
-            if ((message.NegotiateFlags & NegotiateFlags.NegotiateAnonymous) > 0 ||
-                (String.Equals(message.UserName, "Guest", StringComparison.InvariantCultureIgnoreCase) && IsPasswordEmpty(message) && this.EnableGuestLogin))
+            // See [MS-NLMP] 3.3.1 - NTLM v1 Authentication
+            // Special case for anonymous authentication:
+            if (message.LmChallengeResponse.Length == 1 || message.NtChallengeResponse.Length == 0)
             {
-                int guestIndex = IndexOf("Guest");
-                if (guestIndex >= 0)
-                {
-                    return this[guestIndex];
-                }
-                return null;
+                return true;
             }
 
-            int index = IndexOf(message.UserName);
-            if (index >= 0)
+            if ((message.NegotiateFlags & NegotiateFlags.NegotiateExtendedSecurity) > 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))
+                // 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))
                 {
-                    throw new EmptyPasswordNotAllowedException();
+                    return true;
                 }
 
-                bool success = SSPIHelper.AuthenticateType3Message(m_serverContext, authenticateMessageBytes);
-                if (success)
+                // 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 this[index];
+                    return true;
                 }
-            }
-            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)
-        {
-            // Special case for anonymous authentication, see [MS-NLMP] 3.3.1 - NTLM v1 Authentication
-            if (message.LmChallengeResponse.Length == 1 || message.NtChallengeResponse.Length == 0)
-            {
-                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;
+                    }
+                }
             }
-
-            byte[] clientChallenge = ByteReader.ReadBytes(message.LmChallengeResponse, 0, 8);
-            byte[] emptyPasswordNTLMv1Response = NTAuthentication.ComputeNTLMv1ExtendedSecurityResponse(m_serverChallenge, clientChallenge, String.Empty);
-            if (ByteUtils.AreByteArraysEqual(emptyPasswordNTLMv1Response, message.NtChallengeResponse))
+            else
             {
-                return true;
-            }
+                // NTLM v1:
+                byte[] emptyPasswordLMv1Response = NTAuthentication.ComputeLMv1Response(m_serverChallenge, String.Empty);
+                if (ByteUtils.AreByteArraysEqual(emptyPasswordLMv1Response, 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))
+                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);
             }
         }