Quellcode durchsuchen

GSS-style authentication, additional IGSSMechanism implementations can be provided

Tal Aloni vor 8 Jahren
Ursprung
Commit
a84226abb9

+ 0 - 69
SMBLibrary/Authentication/GSSAPI/GSSAPIHelper.cs

@@ -1,69 +0,0 @@
-/* Copyright (C) 2017 Tal Aloni <tal.aloni.il@gmail.com>. All rights reserved.
- * 
- * You can redistribute this program and/or modify it under the terms of
- * the GNU Lesser Public License as published by the Free Software Foundation,
- * either version 3 of the License, or (at your option) any later version.
- */
-using System;
-using System.Collections.Generic;
-using Utilities;
-
-namespace SMBLibrary.Authentication.GSSAPI
-{
-    public class GSSAPIHelper
-    {
-        public static readonly byte[] NTLMSSPIdentifier = new byte[] { 0x2b, 0x06, 0x01, 0x04, 0x01, 0x82, 0x37, 0x02, 0x02, 0x0a };
-
-        /// <summary>
-        /// https://msdn.microsoft.com/en-us/library/ms995330.aspx
-        /// </summary>
-        public static byte[] GetNTLMSSPMessage(byte[] tokenBytes)
-        {
-            SimpleProtectedNegotiationToken token = SimpleProtectedNegotiationToken.ReadToken(tokenBytes, 0);
-            if (token != null)
-            {
-                if (token is SimpleProtectedNegotiationTokenInit)
-                {
-                    SimpleProtectedNegotiationTokenInit tokenInit = (SimpleProtectedNegotiationTokenInit)token;
-                    foreach (byte[] identifier in tokenInit.MechanismTypeList)
-                    {
-                        if (ByteUtils.AreByteArraysEqual(identifier, NTLMSSPIdentifier))
-                        {
-                            return tokenInit.MechanismToken;
-                        }
-                    }
-                }
-                else
-                {
-                    SimpleProtectedNegotiationTokenResponse tokenResponse = (SimpleProtectedNegotiationTokenResponse)token;
-                    return tokenResponse.ResponseToken;
-                }
-            }
-            return null;
-        }
-
-        public static byte[] GetGSSTokenInitNTLMSSPBytes()
-        {
-            SimpleProtectedNegotiationTokenInit token = new SimpleProtectedNegotiationTokenInit();
-            token.MechanismTypeList = new List<byte[]>();
-            token.MechanismTypeList.Add(NTLMSSPIdentifier);
-            return SimpleProtectedNegotiationToken.GetTokenBytes(token);
-        }
-
-        public static byte[] GetGSSTokenResponseBytesFromNTLMSSPMessage(byte[] messageBytes)
-        {
-            SimpleProtectedNegotiationTokenResponse token = new SimpleProtectedNegotiationTokenResponse();
-            token.NegState = NegState.AcceptIncomplete;
-            token.SupportedMechanism = NTLMSSPIdentifier;
-            token.ResponseToken = messageBytes;
-            return token.GetBytes();
-        }
-
-        public static byte[] GetGSSTokenAcceptCompletedResponse()
-        {
-            SimpleProtectedNegotiationTokenResponse token = new SimpleProtectedNegotiationTokenResponse();
-            token.NegState = NegState.AcceptCompleted;
-            return token.GetBytes();
-        }
-    }
-}

+ 213 - 0
SMBLibrary/Authentication/GSSAPI/GSSProvider.cs

@@ -0,0 +1,213 @@
+/* Copyright (C) 2017 Tal Aloni <tal.aloni.il@gmail.com>. All rights reserved.
+ * 
+ * You can redistribute this program and/or modify it under the terms of
+ * the GNU Lesser Public License as published by the Free Software Foundation,
+ * either version 3 of the License, or (at your option) any later version.
+ */
+using System;
+using System.Collections.Generic;
+using SMBLibrary.Authentication.NTLM;
+using Utilities;
+
+namespace SMBLibrary.Authentication.GSSAPI
+{
+    public class GSSProvider
+    {
+        public static readonly byte[] NTLMSSPIdentifier = new byte[] { 0x2b, 0x06, 0x01, 0x04, 0x01, 0x82, 0x37, 0x02, 0x02, 0x0a };
+
+        private List<IGSSMechanism> m_mechanisms;
+        private Dictionary<object, IGSSMechanism> m_contextToMechanism = new Dictionary<object, IGSSMechanism>();
+
+        public GSSProvider(IGSSMechanism mechanism)
+        {
+            m_mechanisms = new List<IGSSMechanism>();
+            m_mechanisms.Add(mechanism);
+        }
+
+        public GSSProvider(List<IGSSMechanism> mechanisms)
+        {
+            m_mechanisms = mechanisms;
+        }
+
+        public byte[] GetSPNEGOTokenInitBytes()
+        {
+            SimpleProtectedNegotiationTokenInit token = new SimpleProtectedNegotiationTokenInit();
+            token.MechanismTypeList = new List<byte[]>();
+            foreach (IGSSMechanism mechanism in m_mechanisms)
+            {
+                token.MechanismTypeList.Add(mechanism.Identifier);
+            }
+            return SimpleProtectedNegotiationToken.GetTokenBytes(token);
+        }
+
+        public NTStatus AcceptSecurityContext(ref object context, byte[] inputToken, out byte[] outputToken)
+        {
+            outputToken = null;
+            SimpleProtectedNegotiationToken spnegoToken = SimpleProtectedNegotiationToken.ReadToken(inputToken, 0);
+            if (spnegoToken != null)
+            {
+                if (spnegoToken is SimpleProtectedNegotiationTokenInit)
+                {
+                    SimpleProtectedNegotiationTokenInit tokenInit = (SimpleProtectedNegotiationTokenInit)spnegoToken;
+                    IGSSMechanism mechanism = FindMechanism(tokenInit.MechanismTypeList);
+                    if (mechanism != null)
+                    {
+                        byte[] mechanismOutput;
+                        NTStatus status = mechanism.AcceptSecurityContext(ref context, tokenInit.MechanismToken, out mechanismOutput);
+                        outputToken = GetSPNEGOTokenResponseBytes(mechanismOutput, status, mechanism.Identifier);
+                        m_contextToMechanism[context] = mechanism;
+                        return status;
+                    }
+                    return NTStatus.SEC_E_SECPKG_NOT_FOUND;
+                }
+                else // SimpleProtectedNegotiationTokenResponse
+                {
+                    IGSSMechanism mechanism;
+                    if (!m_contextToMechanism.TryGetValue(context, out mechanism))
+                    {
+                        // We assume that the problem is not with our implementation and that the client has sent
+                        // SimpleProtectedNegotiationTokenResponse without first sending SimpleProtectedNegotiationTokenInit.
+                        return NTStatus.SEC_E_INVALID_TOKEN;
+                    }
+                    SimpleProtectedNegotiationTokenResponse tokenResponse = (SimpleProtectedNegotiationTokenResponse)spnegoToken;
+                    byte[] mechanismOutput;
+                    NTStatus status = mechanism.AcceptSecurityContext(ref context, tokenResponse.ResponseToken, out mechanismOutput);
+                    outputToken = GetSPNEGOTokenResponseBytes(mechanismOutput, status, null);
+                    return status;
+                }
+            }
+            else
+            {
+                // [MS-SMB] The Windows GSS implementation supports raw Kerberos / NTLM messages in the SecurityBlob.
+                // [MS-SMB2] Windows [..] will also accept raw Kerberos messages and implicit NTLM messages as part of GSS authentication.
+                if (AuthenticationMessageUtils.IsSignatureValid(inputToken))
+                {
+                    MessageTypeName messageType = AuthenticationMessageUtils.GetMessageType(inputToken);
+                    IGSSMechanism ntlmAuthenticationProvider = FindMechanism(NTLMSSPIdentifier);
+                    if (ntlmAuthenticationProvider != null)
+                    {
+                        NTStatus status = ntlmAuthenticationProvider.AcceptSecurityContext(ref context, inputToken, out outputToken);
+                        if (messageType == MessageTypeName.Negotiate)
+                        {
+                            m_contextToMechanism[context] = ntlmAuthenticationProvider;
+                        }
+                        return status;
+                    }
+                    else
+                    {
+                        return NTStatus.SEC_E_SECPKG_NOT_FOUND;
+                    }
+                }
+            }
+            return NTStatus.SEC_E_INVALID_TOKEN;
+        }
+
+        public object GetContextAttribute(object context, GSSAttributeName attributeName)
+        {
+            IGSSMechanism mechanism;
+            if (!m_contextToMechanism.TryGetValue(context, out mechanism))
+            {
+                return null;
+            }
+            return mechanism.GetContextAttribute(context, attributeName);
+        }
+
+        public void DeleteSecurityContext(ref object context)
+        {
+            if (context != null)
+            {
+                IGSSMechanism mechanism;
+                if (m_contextToMechanism.TryGetValue(context, out mechanism))
+                {
+                    mechanism.DeleteSecurityContext(ref context);
+                    m_contextToMechanism.Remove(context);
+                }
+            }
+        }
+
+        /// <summary>
+        /// Helper method for legacy implementation.
+        /// </summary>
+        public NTStatus GetNTLMChallengeMessage(out object context, NegotiateMessage negotiateMessage, out ChallengeMessage challengeMessage)
+        {
+            context = null;
+            challengeMessage = null;
+            IGSSMechanism ntlmAuthenticationProvider = FindMechanism(NTLMSSPIdentifier);
+            if (ntlmAuthenticationProvider != null)
+            {
+                byte[] outputToken;
+                NTStatus result = ntlmAuthenticationProvider.AcceptSecurityContext(ref context, negotiateMessage.GetBytes(), out outputToken);
+                challengeMessage = new ChallengeMessage(outputToken);
+                m_contextToMechanism.Add(context, ntlmAuthenticationProvider);
+                return result;
+            }
+            else
+            {
+                return NTStatus.SEC_E_SECPKG_NOT_FOUND;
+            }
+        }
+
+        /// <summary>
+        /// Helper method for legacy implementation.
+        /// </summary>
+        public NTStatus NTLMAuthenticate(object context, AuthenticateMessage authenticateMessage)
+        {
+            IGSSMechanism ntlmAuthenticationProvider = FindMechanism(NTLMSSPIdentifier);
+            if (ntlmAuthenticationProvider != null)
+            {
+                byte[] outputToken;
+                NTStatus result = ntlmAuthenticationProvider.AcceptSecurityContext(ref context, authenticateMessage.GetBytes(), out outputToken);
+                return result;
+            }
+            else
+            {
+                return NTStatus.SEC_E_SECPKG_NOT_FOUND;
+            }
+        }
+
+        public IGSSMechanism FindMechanism(List<byte[]> mechanismIdentifiers)
+        {
+            foreach (byte[] identifier in mechanismIdentifiers)
+            {
+                IGSSMechanism mechanism = FindMechanism(identifier);
+                if (mechanism != null)
+                {
+                    return mechanism;
+                }
+            }
+            return null;
+        }
+
+        public IGSSMechanism FindMechanism(byte[] mechanismIdentifier)
+        {
+            foreach (IGSSMechanism mechanism in m_mechanisms)
+            {
+                if (ByteUtils.AreByteArraysEqual(mechanism.Identifier, mechanismIdentifier))
+                {
+                    return mechanism;
+                }
+            }
+            return null;
+        }
+
+        private static byte[] GetSPNEGOTokenResponseBytes(byte[] mechanismOutput, NTStatus status, byte[] mechanismIdentifier)
+        {
+            SimpleProtectedNegotiationTokenResponse tokenResponse = new SimpleProtectedNegotiationTokenResponse();
+            if (status == NTStatus.STATUS_SUCCESS)
+            {
+                tokenResponse.NegState = NegState.AcceptCompleted;
+            }
+            else if (status == NTStatus.SEC_I_CONTINUE_NEEDED)
+            {
+                tokenResponse.NegState = NegState.AcceptIncomplete;
+            }
+            else
+            {
+                tokenResponse.NegState = NegState.Reject;
+            }
+            tokenResponse.SupportedMechanism = mechanismIdentifier;
+            tokenResponse.ResponseToken = mechanismOutput;
+            return tokenResponse.GetBytes();
+        }
+    }
+}

+ 1 - 0
SMBLibrary/Enums/NTStatus.cs

@@ -7,6 +7,7 @@ namespace SMBLibrary
         SEC_I_CONTINUE_NEEDED = 0x00090312,
         STATUS_OBJECT_NAME_EXISTS = 0x40000000,
         STATUS_NO_MORE_FILES = 0x80000006,
+        SEC_E_SECPKG_NOT_FOUND = 0x80090305,
         SEC_E_INVALID_TOKEN = 0x80090308,
         STATUS_NOT_IMPLEMENTED = 0xC0000002,
         STATUS_INVALID_INFO_CLASS = 0xC0000003,

+ 1 - 1
SMBLibrary/SMBLibrary.csproj

@@ -32,7 +32,7 @@
   </ItemGroup>
   <ItemGroup>
     <Compile Include="Authentication\GSSAPI\Enums\GSSAttributeName.cs" />
-    <Compile Include="Authentication\GSSAPI\GSSAPIHelper.cs" />
+    <Compile Include="Authentication\GSSAPI\GSSProvider.cs" />
     <Compile Include="Authentication\GSSAPI\IGSSMechanism.cs" />
     <Compile Include="Authentication\GSSAPI\SPNEGO\DerEncodingHelper.cs" />
     <Compile Include="Authentication\GSSAPI\SPNEGO\SimpleProtectedNegotiationToken.cs" />

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

@@ -7,7 +7,7 @@
 using System;
 using System.Collections.Generic;
 using System.Text;
-using SMBLibrary.Authentication;
+using SMBLibrary.Authentication.GSSAPI;
 using SMBLibrary.Authentication.NTLM;
 using SMBLibrary.SMB1;
 using Utilities;
@@ -19,7 +19,7 @@ namespace SMBLibrary.Server.SMB1
     /// </summary>
     public class NegotiateHelper
     {
-        internal static NegotiateResponseNTLM GetNegotiateResponse(SMB1Header header, NegotiateRequest request, NTLMAuthenticationProviderBase securityProvider, ConnectionState state)
+        internal static NegotiateResponseNTLM GetNegotiateResponse(SMB1Header header, NegotiateRequest request, GSSProvider securityProvider, ConnectionState state)
         {
             NegotiateResponseNTLM response = new NegotiateResponseNTLM();
 
@@ -40,7 +40,7 @@ namespace SMBLibrary.Server.SMB1
             response.ServerTimeZone = (short)-TimeZone.CurrentTimeZone.GetUtcOffset(DateTime.Now).TotalMinutes;
             NegotiateMessage negotiateMessage = CreateNegotiateMessage();
             ChallengeMessage challengeMessage;
-            NTStatus status = securityProvider.GetChallengeMessage(out state.AuthenticationContext, negotiateMessage, out challengeMessage);
+            NTStatus status = securityProvider.GetNTLMChallengeMessage(out state.AuthenticationContext, negotiateMessage, out challengeMessage);
             if (status == NTStatus.SEC_I_CONTINUE_NEEDED)
             {
                 response.Challenge = challengeMessage.ServerChallenge;

+ 21 - 48
SMBLibrary/Server/SMB1/SessionSetupHelper.cs

@@ -19,14 +19,14 @@ namespace SMBLibrary.Server.SMB1
     /// </summary>
     public class SessionSetupHelper
     {
-        internal static SMB1Command GetSessionSetupResponse(SMB1Header header, SessionSetupAndXRequest request, NTLMAuthenticationProviderBase securityProvider, SMB1ConnectionState state)
+        internal static SMB1Command GetSessionSetupResponse(SMB1Header header, SessionSetupAndXRequest request, GSSProvider securityProvider, SMB1ConnectionState state)
         {
             SessionSetupAndXResponse response = new SessionSetupAndXResponse();
             // 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
             AuthenticateMessage message = CreateAuthenticateMessage(request.AccountName, request.OEMPassword, request.UnicodePassword);
-            header.Status = securityProvider.Authenticate(state.AuthenticationContext, message);
+            header.Status = securityProvider.NTLMAuthenticate(state.AuthenticationContext, message);
             if (header.Status != NTStatus.STATUS_SUCCESS)
             {
                 state.LogToServer(Severity.Information, "User '{0}' failed authentication, NTStatus: {1}", message.UserName, header.Status);
@@ -69,22 +69,24 @@ namespace SMBLibrary.Server.SMB1
             return response;
         }
 
-        internal static SMB1Command GetSessionSetupResponseExtended(SMB1Header header, SessionSetupAndXRequestExtended request, NTLMAuthenticationProviderBase securityProvider, SMB1ConnectionState state)
+        internal static SMB1Command GetSessionSetupResponseExtended(SMB1Header header, SessionSetupAndXRequestExtended request, GSSProvider securityProvider, SMB1ConnectionState state)
         {
             SessionSetupAndXResponseExtended response = new SessionSetupAndXResponseExtended();
 
             // [MS-SMB] The Windows GSS implementation supports raw Kerberos / NTLM messages in the SecurityBlob
-            byte[] messageBytes = request.SecurityBlob;
-            bool isRawMessage = true;
-            if (!AuthenticationMessageUtils.IsSignatureValid(messageBytes))
+            byte[] outputToken;
+            NTStatus status = securityProvider.AcceptSecurityContext(ref state.AuthenticationContext, request.SecurityBlob, out outputToken);
+            if (status != NTStatus.STATUS_SUCCESS && status != NTStatus.SEC_I_CONTINUE_NEEDED)
             {
-                messageBytes = GSSAPIHelper.GetNTLMSSPMessage(request.SecurityBlob);
-                isRawMessage = false;
+                string userName = securityProvider.GetContextAttribute(state.AuthenticationContext, GSSAttributeName.UserName) as string;
+                state.LogToServer(Severity.Information, "User '{0}' failed authentication, NTStatus: {1}", userName, status);
+                header.Status = status;
+                return new ErrorResponse(request.CommandName);
             }
-            if (!AuthenticationMessageUtils.IsSignatureValid(messageBytes))
+
+            if (outputToken != null)
             {
-                header.Status = NTStatus.STATUS_NOT_IMPLEMENTED;
-                return new ErrorResponse(request.CommandName);
+                response.SecurityBlob = outputToken;
             }
 
             // According to [MS-SMB] 3.3.5.3, a UID MUST be allocated if the server returns STATUS_MORE_PROCESSING_REQUIRED
@@ -99,55 +101,26 @@ namespace SMBLibrary.Server.SMB1
                 header.UID = userID.Value;
             }
 
-            MessageTypeName messageType = AuthenticationMessageUtils.GetMessageType(messageBytes);
-            if (messageType == MessageTypeName.Negotiate)
+            if (status == NTStatus.SEC_I_CONTINUE_NEEDED)
             {
-                NegotiateMessage negotiateMessage = new NegotiateMessage(messageBytes);
-                ChallengeMessage challengeMessage;
-                NTStatus status = securityProvider.GetChallengeMessage(out state.AuthenticationContext, negotiateMessage, out challengeMessage);
-                if (status != NTStatus.SEC_I_CONTINUE_NEEDED)
-                {
-                    header.Status = status;
-                    return new ErrorResponse(request.CommandName);
-                }
-
-                if (isRawMessage)
-                {
-                    response.SecurityBlob = challengeMessage.GetBytes();
-                }
-                else
-                {
-                    response.SecurityBlob = GSSAPIHelper.GetGSSTokenResponseBytesFromNTLMSSPMessage(challengeMessage.GetBytes());
-                }
                 header.Status = NTStatus.STATUS_MORE_PROCESSING_REQUIRED;
             }
-            else // MessageTypeName.Authenticate
+            else // header.Status == NTStatus.STATUS_SUCCESS
             {
-                AuthenticateMessage authenticateMessage = new AuthenticateMessage(messageBytes);
-                header.Status = securityProvider.Authenticate(state.AuthenticationContext, authenticateMessage);
-                if (header.Status != NTStatus.STATUS_SUCCESS)
-                {
-                    state.LogToServer(Severity.Information, "User '{0}' failed authentication, NTStatus: {1}", authenticateMessage.UserName, header.Status);
-                    return new ErrorResponse(request.CommandName);
-                }
-
+                string userName = securityProvider.GetContextAttribute(state.AuthenticationContext, GSSAttributeName.UserName) as string;
+                string machineName = securityProvider.GetContextAttribute(state.AuthenticationContext, GSSAttributeName.MachineName) as string;
                 bool? isGuest = securityProvider.GetContextAttribute(state.AuthenticationContext, GSSAttributeName.IsGuest) as bool?;
                 if (!isGuest.HasValue || !isGuest.Value)
                 {
-                    state.LogToServer(Severity.Information, "User '{0}' authenticated successfully.", authenticateMessage.UserName);
-                    state.CreateSession(header.UID, authenticateMessage.UserName, authenticateMessage.WorkStation);
+                    state.LogToServer(Severity.Information, "User '{0}' authenticated successfully.", userName);
+                    state.CreateSession(header.UID, userName, machineName);
                 }
                 else
                 {
-                    state.LogToServer(Severity.Information, "User '{0}' failed authentication, logged in as guest.", authenticateMessage.UserName);
-                    state.CreateSession(header.UID, "Guest", authenticateMessage.WorkStation);
+                    state.LogToServer(Severity.Information, "User '{0}' failed authentication, logged in as guest.", userName);
+                    state.CreateSession(header.UID, "Guest", machineName);
                     response.Action = SessionSetupAction.SetupGuest;
                 }
-
-                if (!isRawMessage)
-                {
-                    response.SecurityBlob = GSSAPIHelper.GetGSSTokenAcceptCompletedResponse();
-                }
             }
             response.NativeOS = String.Empty; // "Windows Server 2003 3790 Service Pack 2"
             response.NativeLanMan = String.Empty; // "Windows Server 2003 5.2"

+ 4 - 4
SMBLibrary/Server/SMB2/NegotiateHelper.cs

@@ -21,7 +21,7 @@ namespace SMBLibrary.Server.SMB2
         public const string SMB2xxxDialect = "SMB 2.???";
 
         // Special case - SMB2 client initially connecting using SMB1
-        internal static SMB2Command GetNegotiateResponse(List<string> smb2Dialects, ConnectionState state, Guid serverGuid)
+        internal static SMB2Command GetNegotiateResponse(List<string> smb2Dialects, GSSProvider securityProvider, ConnectionState state, Guid serverGuid)
         {
             NegotiateResponse response = new NegotiateResponse();
             response.Header.Credits = 1;
@@ -45,11 +45,11 @@ namespace SMBLibrary.Server.SMB2
             response.MaxWriteSize = 65536;
             response.SystemTime = DateTime.Now;
             response.ServerStartTime = DateTime.Today;
-            response.SecurityBuffer = GSSAPIHelper.GetGSSTokenInitNTLMSSPBytes();
+            response.SecurityBuffer = securityProvider.GetSPNEGOTokenInitBytes();
             return response;
         }
 
-        internal static SMB2Command GetNegotiateResponse(NegotiateRequest request, ConnectionState state, Guid serverGuid)
+        internal static SMB2Command GetNegotiateResponse(NegotiateRequest request, GSSProvider securityProvider, ConnectionState state, Guid serverGuid)
         {
             NegotiateResponse response = new NegotiateResponse();
             if (request.Dialects.Contains(SMB2Dialect.SMB210))
@@ -72,7 +72,7 @@ namespace SMBLibrary.Server.SMB2
             response.MaxWriteSize = 65536;
             response.SystemTime = DateTime.Now;
             response.ServerStartTime = DateTime.Today;
-            response.SecurityBuffer = GSSAPIHelper.GetGSSTokenInitNTLMSSPBytes();
+            response.SecurityBuffer = securityProvider.GetSPNEGOTokenInitBytes();
             return response;
         }
 

+ 18 - 44
SMBLibrary/Server/SMB2/SessionSetupHelper.cs

@@ -18,20 +18,22 @@ namespace SMBLibrary.Server.SMB2
     /// </summary>
     public class SessionSetupHelper
     {
-        internal static SMB2Command GetSessionSetupResponse(SessionSetupRequest request, NTLMAuthenticationProviderBase securityProvider, SMB2ConnectionState state)
+        internal static SMB2Command GetSessionSetupResponse(SessionSetupRequest request, GSSProvider securityProvider, SMB2ConnectionState state)
         {
             // [MS-SMB2] Windows [..] will also accept raw Kerberos messages and implicit NTLM messages as part of GSS authentication.
             SessionSetupResponse response = new SessionSetupResponse();
-            byte[] messageBytes = request.SecurityBuffer;
-            bool isRawMessage = true;
-            if (!AuthenticationMessageUtils.IsSignatureValid(messageBytes))
+            byte[] outputToken;
+            NTStatus status = securityProvider.AcceptSecurityContext(ref state.AuthenticationContext, request.SecurityBuffer, out outputToken);
+            if (status != NTStatus.STATUS_SUCCESS && status != NTStatus.SEC_I_CONTINUE_NEEDED)
             {
-                messageBytes = GSSAPIHelper.GetNTLMSSPMessage(request.SecurityBuffer);
-                isRawMessage = false;
+                string userName = securityProvider.GetContextAttribute(state.AuthenticationContext, GSSAttributeName.UserName) as string;
+                state.LogToServer(Severity.Information, "User '{0}' failed authentication, NTStatus: {1}", userName, status);
+                return new ErrorResponse(request.CommandName, status);
             }
-            if (!AuthenticationMessageUtils.IsSignatureValid(messageBytes))
+
+            if (outputToken != null)
             {
-                return new ErrorResponse(request.CommandName, NTStatus.STATUS_NOT_SUPPORTED);
+                response.SecurityBuffer = outputToken;
             }
 
             // According to [MS-SMB2] 3.3.5.5.3, response.Header.SessionID must be allocated if the server returns STATUS_MORE_PROCESSING_REQUIRED
@@ -45,54 +47,26 @@ namespace SMBLibrary.Server.SMB2
                 response.Header.SessionID = sessionID.Value;
             }
 
-            MessageTypeName messageType = AuthenticationMessageUtils.GetMessageType(messageBytes);
-            if (messageType == MessageTypeName.Negotiate)
+            if (status == NTStatus.SEC_I_CONTINUE_NEEDED)
             {
-                NegotiateMessage negotiateMessage = new NegotiateMessage(messageBytes);
-                ChallengeMessage challengeMessage;
-                NTStatus status = securityProvider.GetChallengeMessage(out state.AuthenticationContext, negotiateMessage, out challengeMessage);
-                if (status != NTStatus.SEC_I_CONTINUE_NEEDED)
-                {
-                    return new ErrorResponse(request.CommandName, status);
-                }
-
-                if (isRawMessage)
-                {
-                    response.SecurityBuffer = challengeMessage.GetBytes();
-                }
-                else
-                {
-                    response.SecurityBuffer = GSSAPIHelper.GetGSSTokenResponseBytesFromNTLMSSPMessage(challengeMessage.GetBytes());
-                }
                 response.Header.Status = NTStatus.STATUS_MORE_PROCESSING_REQUIRED;
             }
-            else // MessageTypeName.Authenticate
+            else // status == STATUS_SUCCESS
             {
-                AuthenticateMessage authenticateMessage = new AuthenticateMessage(messageBytes);
-                NTStatus loginStatus = securityProvider.Authenticate(state.AuthenticationContext, authenticateMessage);
-                if (loginStatus != NTStatus.STATUS_SUCCESS)
-                {
-                    state.LogToServer(Severity.Information, "User '{0}' failed authentication, NTStatus: {1}", authenticateMessage.UserName, loginStatus);
-                    return new ErrorResponse(request.CommandName, loginStatus);
-                }
-
+                string userName = securityProvider.GetContextAttribute(state.AuthenticationContext, GSSAttributeName.UserName) as string;
+                string machineName = securityProvider.GetContextAttribute(state.AuthenticationContext, GSSAttributeName.MachineName) as string;
                 bool? isGuest = securityProvider.GetContextAttribute(state.AuthenticationContext, GSSAttributeName.IsGuest) as bool?;
                 if (!isGuest.HasValue || !isGuest.Value)
                 {
-                    state.LogToServer(Severity.Information, "User '{0}' authenticated successfully.", authenticateMessage.UserName);
-                    state.CreateSession(request.Header.SessionID, authenticateMessage.UserName, authenticateMessage.WorkStation);
+                    state.LogToServer(Severity.Information, "User '{0}' authenticated successfully.", userName);
+                    state.CreateSession(request.Header.SessionID, userName, machineName);
                 }
                 else
                 {
-                    state.LogToServer(Severity.Information, "User '{0}' failed authentication, logged in as guest.", authenticateMessage.UserName);
-                    state.CreateSession(request.Header.SessionID, "Guest", authenticateMessage.WorkStation);
+                    state.LogToServer(Severity.Information, "User '{0}' failed authentication, logged in as guest.", userName);
+                    state.CreateSession(request.Header.SessionID, "Guest", machineName);
                     response.SessionFlags = SessionFlags.IsGuest;
                 }
-
-                if (!isRawMessage)
-                {
-                    response.SecurityBuffer = GSSAPIHelper.GetGSSTokenAcceptCompletedResponse();
-                }
             }
             return response;
         }

+ 1 - 1
SMBLibrary/Server/SMBServer.SMB2.cs

@@ -74,7 +74,7 @@ namespace SMBLibrary.Server
                 if (command is NegotiateRequest)
                 {
                     NegotiateRequest request = (NegotiateRequest)command;
-                    SMB2Command response = NegotiateHelper.GetNegotiateResponse(request, state, m_serverGuid);
+                    SMB2Command response = NegotiateHelper.GetNegotiateResponse(request, m_securityProvider, state, m_serverGuid);
                     if (state.ServerDialect != SMBDialect.NotSet)
                     {
                         state = new SMB2ConnectionState(state, AllocatePersistentFileID);

+ 5 - 5
SMBLibrary/Server/SMBServer.cs

@@ -8,7 +8,7 @@ using System;
 using System.Collections.Generic;
 using System.Net;
 using System.Net.Sockets;
-using SMBLibrary.Authentication.NTLM;
+using SMBLibrary.Authentication.GSSAPI;
 using SMBLibrary.NetBios;
 using SMBLibrary.Services;
 using SMBLibrary.SMB1;
@@ -25,7 +25,7 @@ namespace SMBLibrary.Server
         public const bool EnableExtendedSecurity = true;
 
         private ShareCollection m_shares; // e.g. Shared folders
-        private NTLMAuthenticationProviderBase m_securityProvider;
+        private GSSProvider m_securityProvider;
         private NamedPipeShare m_services; // Named pipes
         private IPAddress m_serverAddress;
         private SMBTransportType m_transport;
@@ -38,11 +38,11 @@ namespace SMBLibrary.Server
 
         public event EventHandler<LogEntry> OnLogEntry;
 
-        public SMBServer(ShareCollection shares, NTLMAuthenticationProviderBase securityProvider, IPAddress serverAddress, SMBTransportType transport) : this(shares, securityProvider, serverAddress, transport, true, true)
+        public SMBServer(ShareCollection shares, GSSProvider securityProvider, IPAddress serverAddress, SMBTransportType transport) : this(shares, securityProvider, serverAddress, transport, true, true)
         {
         }
 
-        public SMBServer(ShareCollection shares, NTLMAuthenticationProviderBase securityProvider, IPAddress serverAddress, SMBTransportType transport, bool enableSMB1, bool enableSMB2)
+        public SMBServer(ShareCollection shares, GSSProvider securityProvider, IPAddress serverAddress, SMBTransportType transport, bool enableSMB1, bool enableSMB2)
         {
             m_shares = shares;
             m_securityProvider = securityProvider;
@@ -245,7 +245,7 @@ namespace SMBLibrary.Server
                         List<string> smb2Dialects = SMB2.NegotiateHelper.FindSMB2Dialects(message);
                         if (smb2Dialects.Count > 0)
                         {
-                            SMB2Command response = SMB2.NegotiateHelper.GetNegotiateResponse(smb2Dialects, state, m_serverGuid);
+                            SMB2Command response = SMB2.NegotiateHelper.GetNegotiateResponse(smb2Dialects, m_securityProvider, state, m_serverGuid);
                             if (state.ServerDialect != SMBDialect.NotSet)
                             {
                                 state = new SMB2ConnectionState(state, AllocatePersistentFileID);

+ 7 - 6
SMBServer/ServerUI.cs

@@ -17,6 +17,7 @@ using System.Text;
 using System.Windows.Forms;
 using System.Xml;
 using SMBLibrary;
+using SMBLibrary.Authentication.GSSAPI;
 using SMBLibrary.Authentication.NTLM;
 using SMBLibrary.Server;
 using SMBLibrary.Win32.Security;
@@ -62,11 +63,10 @@ namespace SMBServer
                 transportType = SMBTransportType.DirectTCPTransport;
             }
 
-            NTLMAuthenticationProviderBase provider;
+            NTLMAuthenticationProviderBase authenticationMechanism;
             if (chkIntegratedWindowsAuthentication.Checked)
             {
-                provider = new IntegratedNTLMAuthenticationProvider();
-                
+                authenticationMechanism = new IntegratedNTLMAuthenticationProvider();
             }
             else
             {
@@ -80,8 +80,8 @@ namespace SMBServer
                     MessageBox.Show("Cannot read " + SettingsFileName, "Error");
                     return;
                 }
-                
-                provider = new IndependentNTLMAuthenticationProvider(users.GetUserPassword);
+
+                authenticationMechanism = new IndependentNTLMAuthenticationProvider(users.GetUserPassword);
             }
 
 
@@ -96,7 +96,8 @@ namespace SMBServer
                 return;
             }
 
-            m_server = new SMBLibrary.Server.SMBServer(shares, provider, serverAddress, transportType, chkSMB1.Checked, chkSMB2.Checked);
+            GSSProvider securityProvider = new GSSProvider(authenticationMechanism);
+            m_server = new SMBLibrary.Server.SMBServer(shares, securityProvider, serverAddress, transportType, chkSMB1.Checked, chkSMB2.Checked);
             m_server.OnLogEntry += new EventHandler<LogEntry>(Server_OnLogEntry);
 
             try