Browse Source

NTLMAuthenticationProviderBase: Return NTStatus instead of Win32Error

Tal Aloni 8 years ago
parent
commit
45dc792558

+ 15 - 10
SMBLibrary/Authentication/NTLM/IndependentNTLMAuthenticationProvider.cs

@@ -40,7 +40,7 @@ namespace SMBLibrary.Authentication.NTLM
             m_GetUserPassword = getUserPassword;
         }
 
-        public override Win32Error GetChallengeMessage(out object context, NegotiateMessage negotiateMessage, out ChallengeMessage challengeMessage)
+        public override NTStatus GetChallengeMessage(out object context, NegotiateMessage negotiateMessage, out ChallengeMessage challengeMessage)
         {
             byte[] serverChallenge = GenerateServerChallenge();
             context = new AuthContext(negotiateMessage.Workstation, serverChallenge);
@@ -71,15 +71,20 @@ namespace SMBLibrary.Authentication.NTLM
             challengeMessage.ServerChallenge = serverChallenge;
             challengeMessage.TargetInfo = AVPairUtils.GetAVPairSequence(Environment.MachineName, Environment.MachineName);
             challengeMessage.Version = NTLMVersion.Server2003;
-            return Win32Error.ERROR_SUCCESS;
+            return NTStatus.SEC_I_CONTINUE_NEEDED;
         }
 
-        public override Win32Error Authenticate(object context, AuthenticateMessage message)
+        public override NTStatus Authenticate(object context, AuthenticateMessage message)
         {
             AuthContext authContext = context as AuthContext;
             if (authContext == null)
             {
-                return Win32Error.ERROR_NO_TOKEN;
+                // There are two possible reasons for authContext to be null:
+                // 1. We have a bug in our implementation, let's assume that's not the case,
+                //    according to [MS-SMB2] 3.3.5.5.1 we aren't allowed to return SEC_E_INVALID_HANDLE anyway.
+                // 2. The client sent AuthenticateMessage without sending NegotiateMessage first,
+                //    in this case the correct response is SEC_E_INVALID_TOKEN.
+                return NTStatus.SEC_E_INVALID_TOKEN;
             }
 
             authContext.UserName = message.UserName;
@@ -89,11 +94,11 @@ namespace SMBLibrary.Authentication.NTLM
                 if (this.EnableGuestLogin)
                 {
                     authContext.IsGuest = true;
-                    return Win32Error.ERROR_SUCCESS;
+                    return NTStatus.STATUS_SUCCESS;
                 }
                 else
                 {
-                    return Win32Error.ERROR_LOGON_FAILURE;
+                    return NTStatus.STATUS_LOGON_FAILURE;
                 }
             }
 
@@ -103,11 +108,11 @@ namespace SMBLibrary.Authentication.NTLM
                 if (this.EnableGuestLogin)
                 {
                     authContext.IsGuest = true;
-                    return Win32Error.ERROR_SUCCESS;
+                    return NTStatus.STATUS_SUCCESS;
                 }
                 else
                 {
-                    return Win32Error.ERROR_LOGON_FAILURE;
+                    return NTStatus.STATUS_LOGON_FAILURE;
                 }
             }
 
@@ -133,11 +138,11 @@ namespace SMBLibrary.Authentication.NTLM
 
             if (success)
             {
-                return Win32Error.ERROR_SUCCESS;
+                return NTStatus.STATUS_SUCCESS;
             }
             else
             {
-                return Win32Error.ERROR_LOGON_FAILURE;
+                return NTStatus.STATUS_LOGON_FAILURE;
             }
         }
 

+ 2 - 2
SMBLibrary/Authentication/NTLM/NTLMAuthenticationProviderBase.cs

@@ -12,9 +12,9 @@ namespace SMBLibrary.Authentication.NTLM
 {
     public abstract class NTLMAuthenticationProviderBase
     {
-        public abstract Win32Error GetChallengeMessage(out object context, NegotiateMessage negotiateMessage, out ChallengeMessage challengeMessage);
+        public abstract NTStatus GetChallengeMessage(out object context, NegotiateMessage negotiateMessage, out ChallengeMessage challengeMessage);
 
-        public abstract Win32Error Authenticate(object context, AuthenticateMessage authenticateMessage);
+        public abstract NTStatus Authenticate(object context, AuthenticateMessage authenticateMessage);
 
         public abstract void DeleteSecurityContext(ref object context);
 

+ 2 - 0
SMBLibrary/Enums/NTStatus.cs

@@ -4,8 +4,10 @@ namespace SMBLibrary
     public enum NTStatus : uint
     {
         STATUS_SUCCESS = 0x00000000,
+        SEC_I_CONTINUE_NEEDED = 0x00090312,
         STATUS_OBJECT_NAME_EXISTS = 0x40000000,
         STATUS_NO_MORE_FILES = 0x80000006,
+        SEC_E_INVALID_TOKEN = 0x80090308,
         STATUS_NOT_IMPLEMENTED = 0xC0000002,
         STATUS_INVALID_INFO_CLASS = 0xC0000003,
         STATUS_INVALID_HANDLE = 0xC0000008,

+ 0 - 1
SMBLibrary/SMBLibrary.csproj

@@ -186,7 +186,6 @@
     <Compile Include="Server\ConnectionState\SMB2Session.cs" />
     <Compile Include="Server\Exceptions\InvalidRequestException.cs" />
     <Compile Include="Server\Exceptions\UnsupportedInformationLevelException.cs" />
-    <Compile Include="Server\Helpers\LogonHelper.cs" />
     <Compile Include="Server\Helpers\ServerPathUtils.cs" />
     <Compile Include="Server\NameServer.cs" />
     <Compile Include="Server\Shares\FileSystemShare.cs" />

+ 0 - 42
SMBLibrary/Server/Helpers/LogonHelper.cs

@@ -1,42 +0,0 @@
-/* Copyright (C) 2014-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 Utilities;
-
-namespace SMBLibrary.Server
-{
-    public class LogonHelper
-    {
-        public static NTStatus ToNTStatus(Win32Error errorCode)
-        {
-            switch (errorCode)
-            {
-                case Win32Error.ERROR_ACCOUNT_RESTRICTION:
-                    return NTStatus.STATUS_ACCOUNT_RESTRICTION;
-                case Win32Error.ERROR_INVALID_LOGON_HOURS:
-                    return NTStatus.STATUS_INVALID_LOGON_HOURS;
-                case Win32Error.ERROR_INVALID_WORKSTATION:
-                    return NTStatus.STATUS_INVALID_WORKSTATION;
-                case Win32Error.ERROR_PASSWORD_EXPIRED:
-                    return NTStatus.STATUS_PASSWORD_EXPIRED;
-                case Win32Error.ERROR_ACCOUNT_DISABLED:
-                    return NTStatus.STATUS_ACCOUNT_DISABLED;
-                case Win32Error.ERROR_LOGON_TYPE_NOT_GRANTED:
-                    return NTStatus.STATUS_LOGON_TYPE_NOT_GRANTED;
-                case Win32Error.ERROR_ACCOUNT_EXPIRED:
-                    return NTStatus.STATUS_ACCOUNT_EXPIRED;
-                case Win32Error.ERROR_PASSWORD_MUST_CHANGE:
-                    return NTStatus.STATUS_PASSWORD_MUST_CHANGE;
-                case Win32Error.ERROR_ACCOUNT_LOCKED_OUT:
-                    return NTStatus.STATUS_ACCOUNT_LOCKED_OUT;    
-                default:
-                    return NTStatus.STATUS_LOGON_FAILURE;
-            }
-        }
-    }
-}

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

@@ -40,8 +40,8 @@ namespace SMBLibrary.Server.SMB1
             response.ServerTimeZone = (short)-TimeZone.CurrentTimeZone.GetUtcOffset(DateTime.Now).TotalMinutes;
             NegotiateMessage negotiateMessage = CreateNegotiateMessage();
             ChallengeMessage challengeMessage;
-            Win32Error status = securityProvider.GetChallengeMessage(out state.AuthenticationContext, negotiateMessage, out challengeMessage);
-            if (status == Win32Error.ERROR_SUCCESS)
+            NTStatus status = securityProvider.GetChallengeMessage(out state.AuthenticationContext, negotiateMessage, out challengeMessage);
+            if (status == NTStatus.SEC_I_CONTINUE_NEEDED)
             {
                 response.Challenge = challengeMessage.ServerChallenge;
             }

+ 9 - 11
SMBLibrary/Server/SMB1/SessionSetupHelper.cs

@@ -26,11 +26,10 @@ namespace SMBLibrary.Server.SMB1
             // 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
             AuthenticateMessage message = CreateAuthenticateMessage(request.AccountName, request.OEMPassword, request.UnicodePassword);
-            Win32Error loginStatus = securityProvider.Authenticate(state.AuthenticationContext, message);
-            if (loginStatus != Win32Error.ERROR_SUCCESS)
+            header.Status = securityProvider.Authenticate(state.AuthenticationContext, message);
+            if (header.Status != NTStatus.STATUS_SUCCESS)
             {
-                state.LogToServer(Severity.Information, "User '{0}' failed authentication, Win32 error: {1}", message.UserName, loginStatus);
-                header.Status = LogonHelper.ToNTStatus(loginStatus);
+                state.LogToServer(Severity.Information, "User '{0}' failed authentication, NTStatus: {1}", message.UserName, header.Status);
                 return new ErrorResponse(request.CommandName);
             }
 
@@ -105,10 +104,10 @@ namespace SMBLibrary.Server.SMB1
             {
                 NegotiateMessage negotiateMessage = new NegotiateMessage(messageBytes);
                 ChallengeMessage challengeMessage;
-                Win32Error status = securityProvider.GetChallengeMessage(out state.AuthenticationContext, negotiateMessage, out challengeMessage);
-                if (status != Win32Error.ERROR_SUCCESS)
+                NTStatus status = securityProvider.GetChallengeMessage(out state.AuthenticationContext, negotiateMessage, out challengeMessage);
+                if (status != NTStatus.SEC_I_CONTINUE_NEEDED)
                 {
-                    header.Status = NTStatus.STATUS_LOGON_FAILURE;
+                    header.Status = status;
                     return new ErrorResponse(request.CommandName);
                 }
 
@@ -125,11 +124,10 @@ namespace SMBLibrary.Server.SMB1
             else // MessageTypeName.Authenticate
             {
                 AuthenticateMessage authenticateMessage = new AuthenticateMessage(messageBytes);
-                Win32Error loginStatus = securityProvider.Authenticate(state.AuthenticationContext, authenticateMessage);
-                if (loginStatus != Win32Error.ERROR_SUCCESS)
+                header.Status = securityProvider.Authenticate(state.AuthenticationContext, authenticateMessage);
+                if (header.Status != NTStatus.STATUS_SUCCESS)
                 {
-                    state.LogToServer(Severity.Information, "User '{0}' failed authentication, Win32 error: {1}", authenticateMessage.UserName, loginStatus);
-                    header.Status = LogonHelper.ToNTStatus(loginStatus);
+                    state.LogToServer(Severity.Information, "User '{0}' failed authentication, NTStatus: {1}", authenticateMessage.UserName, header.Status);
                     return new ErrorResponse(request.CommandName);
                 }
 

+ 7 - 8
SMBLibrary/Server/SMB2/SessionSetupHelper.cs

@@ -50,10 +50,10 @@ namespace SMBLibrary.Server.SMB2
             {
                 NegotiateMessage negotiateMessage = new NegotiateMessage(messageBytes);
                 ChallengeMessage challengeMessage;
-                Win32Error status = securityProvider.GetChallengeMessage(out state.AuthenticationContext, negotiateMessage, out challengeMessage);
-                if (status != Win32Error.ERROR_SUCCESS)
+                NTStatus status = securityProvider.GetChallengeMessage(out state.AuthenticationContext, negotiateMessage, out challengeMessage);
+                if (status != NTStatus.SEC_I_CONTINUE_NEEDED)
                 {
-                    return new ErrorResponse(request.CommandName, NTStatus.STATUS_LOGON_FAILURE);
+                    return new ErrorResponse(request.CommandName, status);
                 }
 
                 if (isRawMessage)
@@ -69,12 +69,11 @@ namespace SMBLibrary.Server.SMB2
             else // MessageTypeName.Authenticate
             {
                 AuthenticateMessage authenticateMessage = new AuthenticateMessage(messageBytes);
-                Win32Error loginStatus = securityProvider.Authenticate(state.AuthenticationContext, authenticateMessage);
-                if (loginStatus != Win32Error.ERROR_SUCCESS)
+                NTStatus loginStatus = securityProvider.Authenticate(state.AuthenticationContext, authenticateMessage);
+                if (loginStatus != NTStatus.STATUS_SUCCESS)
                 {
-                    state.LogToServer(Severity.Information, "User '{0}' failed authentication, Win32 error: {1}", authenticateMessage.UserName, loginStatus);
-                    NTStatus status = LogonHelper.ToNTStatus(loginStatus);
-                    return new ErrorResponse(request.CommandName, status);
+                    state.LogToServer(Severity.Information, "User '{0}' failed authentication, NTStatus: {1}", authenticateMessage.UserName, loginStatus);
+                    return new ErrorResponse(request.CommandName, loginStatus);
                 }
 
                 bool? isGuest = securityProvider.GetContextAttribute(state.AuthenticationContext, GSSAttributeName.IsGuest) as bool?;

+ 47 - 11
SMBLibrary/Win32/IntegratedNTLMAuthenticationProvider.cs

@@ -33,7 +33,7 @@ namespace SMBLibrary.Win32.Security
             }
         }
 
-        public override Win32Error GetChallengeMessage(out object context, NegotiateMessage negotiateMessage, out ChallengeMessage challengeMessage)
+        public override NTStatus GetChallengeMessage(out object context, NegotiateMessage negotiateMessage, out ChallengeMessage challengeMessage)
         {
             byte[] negotiateMessageBytes = negotiateMessage.GetBytes();
             SecHandle serverContext;
@@ -46,12 +46,13 @@ namespace SMBLibrary.Win32.Security
             {
                 context = null;
                 challengeMessage = null;
-                return (Win32Error)Marshal.GetLastWin32Error();
+                // We assume that the problem is not with our implementation.
+                return NTStatus.SEC_E_INVALID_TOKEN;
             }
 
             context = new AuthContext(serverContext, negotiateMessage.Workstation);
             challengeMessage = new ChallengeMessage(challengeMessageBytes);
-            return Win32Error.ERROR_SUCCESS;
+            return NTStatus.SEC_I_CONTINUE_NEEDED;
         }
 
         /// <summary>
@@ -59,12 +60,17 @@ namespace SMBLibrary.Win32.Security
         /// 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 override Win32Error Authenticate(object context, AuthenticateMessage message)
+        public override NTStatus Authenticate(object context, AuthenticateMessage message)
         {
             AuthContext authContext = context as AuthContext;
             if (authContext == null)
             {
-                return Win32Error.ERROR_NO_TOKEN;
+                // There are two possible reasons for authContext to be null:
+                // 1. We have a bug in our implementation, let's assume that's not the case,
+                //    according to [MS-SMB2] 3.3.5.5.1 we aren't allowed to return SEC_E_INVALID_HANDLE anyway.
+                // 2. The client sent AuthenticateMessage without sending NegotiateMessage first,
+                //    in this case the correct response is SEC_E_INVALID_TOKEN.
+                return NTStatus.SEC_E_INVALID_TOKEN;
             }
 
             authContext.UserName = message.UserName;
@@ -74,11 +80,11 @@ namespace SMBLibrary.Win32.Security
                 if (this.EnableGuestLogin)
                 {
                     authContext.IsGuest = true;
-                    return Win32Error.ERROR_SUCCESS;
+                    return NTStatus.STATUS_SUCCESS;
                 }
                 else
                 {
-                    return Win32Error.ERROR_LOGON_FAILURE;
+                    return NTStatus.STATUS_LOGON_FAILURE;
                 }
             }
 
@@ -90,12 +96,13 @@ namespace SMBLibrary.Win32.Security
             }
             catch (Exception)
             {
-                return (Win32Error)Marshal.GetLastWin32Error();
+                // We assume that the problem is not with our implementation.
+                return NTStatus.SEC_E_INVALID_TOKEN;
             }
 
             if (success)
             {
-                return Win32Error.ERROR_SUCCESS;
+                return NTStatus.STATUS_SUCCESS;
             }
             else
             {
@@ -110,11 +117,11 @@ namespace SMBLibrary.Win32.Security
                 if (allowFallback && this.EnableGuestLogin)
                 {
                     authContext.IsGuest = true;
-                    return Win32Error.ERROR_SUCCESS;
+                    return NTStatus.STATUS_SUCCESS;
                 }
                 else
                 {
-                    return result;
+                    return ToNTStatus(result);
                 }
             }
         }
@@ -172,5 +179,34 @@ namespace SMBLibrary.Win32.Security
         {
             return NetworkAPI.IsUserExists(userName);
         }
+
+        public static NTStatus ToNTStatus(Win32Error errorCode)
+        {
+            switch (errorCode)
+            {
+                case Win32Error.ERROR_NO_TOKEN:
+                    return NTStatus.SEC_E_INVALID_TOKEN;
+                case Win32Error.ERROR_ACCOUNT_RESTRICTION:
+                    return NTStatus.STATUS_ACCOUNT_RESTRICTION;
+                case Win32Error.ERROR_INVALID_LOGON_HOURS:
+                    return NTStatus.STATUS_INVALID_LOGON_HOURS;
+                case Win32Error.ERROR_INVALID_WORKSTATION:
+                    return NTStatus.STATUS_INVALID_WORKSTATION;
+                case Win32Error.ERROR_PASSWORD_EXPIRED:
+                    return NTStatus.STATUS_PASSWORD_EXPIRED;
+                case Win32Error.ERROR_ACCOUNT_DISABLED:
+                    return NTStatus.STATUS_ACCOUNT_DISABLED;
+                case Win32Error.ERROR_LOGON_TYPE_NOT_GRANTED:
+                    return NTStatus.STATUS_LOGON_TYPE_NOT_GRANTED;
+                case Win32Error.ERROR_ACCOUNT_EXPIRED:
+                    return NTStatus.STATUS_ACCOUNT_EXPIRED;
+                case Win32Error.ERROR_PASSWORD_MUST_CHANGE:
+                    return NTStatus.STATUS_PASSWORD_MUST_CHANGE;
+                case Win32Error.ERROR_ACCOUNT_LOCKED_OUT:
+                    return NTStatus.STATUS_ACCOUNT_LOCKED_OUT;
+                default:
+                    return NTStatus.STATUS_LOGON_FAILURE;
+            }
+        }
     }
 }