diff --git a/SMBLibrary/Authentication/GSSAPI/GSSAPIHelper.cs b/SMBLibrary/Authentication/GSSAPI/GSSAPIHelper.cs deleted file mode 100644 index ff4d03e..0000000 --- a/SMBLibrary/Authentication/GSSAPI/GSSAPIHelper.cs +++ /dev/null @@ -1,69 +0,0 @@ -/* Copyright (C) 2017 Tal Aloni . 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 }; - - /// - /// https://msdn.microsoft.com/en-us/library/ms995330.aspx - /// - 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(); - 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(); - } - } -} diff --git a/SMBLibrary/Authentication/GSSAPI/GSSProvider.cs b/SMBLibrary/Authentication/GSSAPI/GSSProvider.cs new file mode 100644 index 0000000..9def58e --- /dev/null +++ b/SMBLibrary/Authentication/GSSAPI/GSSProvider.cs @@ -0,0 +1,213 @@ +/* Copyright (C) 2017 Tal Aloni . 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 m_mechanisms; + private Dictionary m_contextToMechanism = new Dictionary(); + + public GSSProvider(IGSSMechanism mechanism) + { + m_mechanisms = new List(); + m_mechanisms.Add(mechanism); + } + + public GSSProvider(List mechanisms) + { + m_mechanisms = mechanisms; + } + + public byte[] GetSPNEGOTokenInitBytes() + { + SimpleProtectedNegotiationTokenInit token = new SimpleProtectedNegotiationTokenInit(); + token.MechanismTypeList = new List(); + 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); + } + } + } + + /// + /// Helper method for legacy implementation. + /// + 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; + } + } + + /// + /// Helper method for legacy implementation. + /// + 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 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(); + } + } +} diff --git a/SMBLibrary/Enums/NTStatus.cs b/SMBLibrary/Enums/NTStatus.cs index 15cdc80..dd8443c 100644 --- a/SMBLibrary/Enums/NTStatus.cs +++ b/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, diff --git a/SMBLibrary/SMBLibrary.csproj b/SMBLibrary/SMBLibrary.csproj index e97f6f0..611491e 100644 --- a/SMBLibrary/SMBLibrary.csproj +++ b/SMBLibrary/SMBLibrary.csproj @@ -32,7 +32,7 @@ - + diff --git a/SMBLibrary/Server/SMB1/NegotiateHelper.cs b/SMBLibrary/Server/SMB1/NegotiateHelper.cs index 7fe5cf5..3c5b627 100644 --- a/SMBLibrary/Server/SMB1/NegotiateHelper.cs +++ b/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 /// 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; diff --git a/SMBLibrary/Server/SMB1/SessionSetupHelper.cs b/SMBLibrary/Server/SMB1/SessionSetupHelper.cs index 99701b4..eec794d 100644 --- a/SMBLibrary/Server/SMB1/SessionSetupHelper.cs +++ b/SMBLibrary/Server/SMB1/SessionSetupHelper.cs @@ -19,14 +19,14 @@ namespace SMBLibrary.Server.SMB1 /// 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,24 +69,26 @@ 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; - } - if (!AuthenticationMessageUtils.IsSignatureValid(messageBytes)) - { - header.Status = NTStatus.STATUS_NOT_IMPLEMENTED; + 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 (outputToken != null) + { + response.SecurityBlob = outputToken; + } + // According to [MS-SMB] 3.3.5.3, a UID MUST be allocated if the server returns STATUS_MORE_PROCESSING_REQUIRED if (header.UID == 0) { @@ -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" diff --git a/SMBLibrary/Server/SMB2/NegotiateHelper.cs b/SMBLibrary/Server/SMB2/NegotiateHelper.cs index 33d6b8e..8ee861d 100644 --- a/SMBLibrary/Server/SMB2/NegotiateHelper.cs +++ b/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 smb2Dialects, ConnectionState state, Guid serverGuid) + internal static SMB2Command GetNegotiateResponse(List 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; } diff --git a/SMBLibrary/Server/SMB2/SessionSetupHelper.cs b/SMBLibrary/Server/SMB2/SessionSetupHelper.cs index 386c47d..ef87fa4 100644 --- a/SMBLibrary/Server/SMB2/SessionSetupHelper.cs +++ b/SMBLibrary/Server/SMB2/SessionSetupHelper.cs @@ -18,20 +18,22 @@ namespace SMBLibrary.Server.SMB2 /// 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; } diff --git a/SMBLibrary/Server/SMBServer.SMB2.cs b/SMBLibrary/Server/SMBServer.SMB2.cs index 5eaa6fa..2ce6206 100644 --- a/SMBLibrary/Server/SMBServer.SMB2.cs +++ b/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); diff --git a/SMBLibrary/Server/SMBServer.cs b/SMBLibrary/Server/SMBServer.cs index ebe03d1..cfda9b5 100644 --- a/SMBLibrary/Server/SMBServer.cs +++ b/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 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 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); diff --git a/SMBServer/ServerUI.cs b/SMBServer/ServerUI.cs index 80a4d2f..f11f784 100644 --- a/SMBServer/ServerUI.cs +++ b/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(Server_OnLogEntry); try