GSS-style authentication, additional IGSSMechanism implementations can be provided

This commit is contained in:
Tal Aloni 2017-02-18 16:10:03 +02:00
parent d4acf5900e
commit a84226abb9
11 changed files with 275 additions and 182 deletions

View file

@ -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();
}
}
}

View file

@ -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();
}
}
}

View file

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

View file

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

View file

@ -7,7 +7,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Text; using System.Text;
using SMBLibrary.Authentication; using SMBLibrary.Authentication.GSSAPI;
using SMBLibrary.Authentication.NTLM; using SMBLibrary.Authentication.NTLM;
using SMBLibrary.SMB1; using SMBLibrary.SMB1;
using Utilities; using Utilities;
@ -19,7 +19,7 @@ namespace SMBLibrary.Server.SMB1
/// </summary> /// </summary>
public class NegotiateHelper 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(); NegotiateResponseNTLM response = new NegotiateResponseNTLM();
@ -40,7 +40,7 @@ namespace SMBLibrary.Server.SMB1
response.ServerTimeZone = (short)-TimeZone.CurrentTimeZone.GetUtcOffset(DateTime.Now).TotalMinutes; response.ServerTimeZone = (short)-TimeZone.CurrentTimeZone.GetUtcOffset(DateTime.Now).TotalMinutes;
NegotiateMessage negotiateMessage = CreateNegotiateMessage(); NegotiateMessage negotiateMessage = CreateNegotiateMessage();
ChallengeMessage challengeMessage; 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) if (status == NTStatus.SEC_I_CONTINUE_NEEDED)
{ {
response.Challenge = challengeMessage.ServerChallenge; response.Challenge = challengeMessage.ServerChallenge;

View file

@ -19,14 +19,14 @@ namespace SMBLibrary.Server.SMB1
/// </summary> /// </summary>
public class SessionSetupHelper 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(); SessionSetupAndXResponse response = new SessionSetupAndXResponse();
// The PrimaryDomain field in the request is used to determine with domain controller should authenticate the user credentials, // The PrimaryDomain field in the request is used to determine with domain controller should authenticate the user credentials,
// However, the domain controller itself does not use this field. // However, the domain controller itself does not use this field.
// See: http://msdn.microsoft.com/en-us/library/windows/desktop/aa378749%28v=vs.85%29.aspx // See: http://msdn.microsoft.com/en-us/library/windows/desktop/aa378749%28v=vs.85%29.aspx
AuthenticateMessage message = CreateAuthenticateMessage(request.AccountName, request.OEMPassword, request.UnicodePassword); 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) if (header.Status != NTStatus.STATUS_SUCCESS)
{ {
state.LogToServer(Severity.Information, "User '{0}' failed authentication, NTStatus: {1}", message.UserName, header.Status); state.LogToServer(Severity.Information, "User '{0}' failed authentication, NTStatus: {1}", message.UserName, header.Status);
@ -69,24 +69,26 @@ namespace SMBLibrary.Server.SMB1
return response; 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(); SessionSetupAndXResponseExtended response = new SessionSetupAndXResponseExtended();
// [MS-SMB] The Windows GSS implementation supports raw Kerberos / NTLM messages in the SecurityBlob // [MS-SMB] The Windows GSS implementation supports raw Kerberos / NTLM messages in the SecurityBlob
byte[] messageBytes = request.SecurityBlob; byte[] outputToken;
bool isRawMessage = true; NTStatus status = securityProvider.AcceptSecurityContext(ref state.AuthenticationContext, request.SecurityBlob, out outputToken);
if (!AuthenticationMessageUtils.IsSignatureValid(messageBytes)) if (status != NTStatus.STATUS_SUCCESS && status != NTStatus.SEC_I_CONTINUE_NEEDED)
{ {
messageBytes = GSSAPIHelper.GetNTLMSSPMessage(request.SecurityBlob); string userName = securityProvider.GetContextAttribute(state.AuthenticationContext, GSSAttributeName.UserName) as string;
isRawMessage = false; state.LogToServer(Severity.Information, "User '{0}' failed authentication, NTStatus: {1}", userName, status);
} header.Status = status;
if (!AuthenticationMessageUtils.IsSignatureValid(messageBytes))
{
header.Status = NTStatus.STATUS_NOT_IMPLEMENTED;
return new ErrorResponse(request.CommandName); 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 // 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) if (header.UID == 0)
{ {
@ -99,55 +101,26 @@ namespace SMBLibrary.Server.SMB1
header.UID = userID.Value; header.UID = userID.Value;
} }
MessageTypeName messageType = AuthenticationMessageUtils.GetMessageType(messageBytes); if (status == NTStatus.SEC_I_CONTINUE_NEEDED)
if (messageType == MessageTypeName.Negotiate)
{ {
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; header.Status = NTStatus.STATUS_MORE_PROCESSING_REQUIRED;
} }
else // MessageTypeName.Authenticate else // header.Status == NTStatus.STATUS_SUCCESS
{ {
AuthenticateMessage authenticateMessage = new AuthenticateMessage(messageBytes); string userName = securityProvider.GetContextAttribute(state.AuthenticationContext, GSSAttributeName.UserName) as string;
header.Status = securityProvider.Authenticate(state.AuthenticationContext, authenticateMessage); string machineName = securityProvider.GetContextAttribute(state.AuthenticationContext, GSSAttributeName.MachineName) as string;
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);
}
bool? isGuest = securityProvider.GetContextAttribute(state.AuthenticationContext, GSSAttributeName.IsGuest) as bool?; bool? isGuest = securityProvider.GetContextAttribute(state.AuthenticationContext, GSSAttributeName.IsGuest) as bool?;
if (!isGuest.HasValue || !isGuest.Value) if (!isGuest.HasValue || !isGuest.Value)
{ {
state.LogToServer(Severity.Information, "User '{0}' authenticated successfully.", authenticateMessage.UserName); state.LogToServer(Severity.Information, "User '{0}' authenticated successfully.", userName);
state.CreateSession(header.UID, authenticateMessage.UserName, authenticateMessage.WorkStation); state.CreateSession(header.UID, userName, machineName);
} }
else else
{ {
state.LogToServer(Severity.Information, "User '{0}' failed authentication, logged in as guest.", authenticateMessage.UserName); state.LogToServer(Severity.Information, "User '{0}' failed authentication, logged in as guest.", userName);
state.CreateSession(header.UID, "Guest", authenticateMessage.WorkStation); state.CreateSession(header.UID, "Guest", machineName);
response.Action = SessionSetupAction.SetupGuest; response.Action = SessionSetupAction.SetupGuest;
} }
if (!isRawMessage)
{
response.SecurityBlob = GSSAPIHelper.GetGSSTokenAcceptCompletedResponse();
}
} }
response.NativeOS = String.Empty; // "Windows Server 2003 3790 Service Pack 2" response.NativeOS = String.Empty; // "Windows Server 2003 3790 Service Pack 2"
response.NativeLanMan = String.Empty; // "Windows Server 2003 5.2" response.NativeLanMan = String.Empty; // "Windows Server 2003 5.2"

View file

@ -21,7 +21,7 @@ namespace SMBLibrary.Server.SMB2
public const string SMB2xxxDialect = "SMB 2.???"; public const string SMB2xxxDialect = "SMB 2.???";
// Special case - SMB2 client initially connecting using SMB1 // 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(); NegotiateResponse response = new NegotiateResponse();
response.Header.Credits = 1; response.Header.Credits = 1;
@ -45,11 +45,11 @@ namespace SMBLibrary.Server.SMB2
response.MaxWriteSize = 65536; response.MaxWriteSize = 65536;
response.SystemTime = DateTime.Now; response.SystemTime = DateTime.Now;
response.ServerStartTime = DateTime.Today; response.ServerStartTime = DateTime.Today;
response.SecurityBuffer = GSSAPIHelper.GetGSSTokenInitNTLMSSPBytes(); response.SecurityBuffer = securityProvider.GetSPNEGOTokenInitBytes();
return response; 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(); NegotiateResponse response = new NegotiateResponse();
if (request.Dialects.Contains(SMB2Dialect.SMB210)) if (request.Dialects.Contains(SMB2Dialect.SMB210))
@ -72,7 +72,7 @@ namespace SMBLibrary.Server.SMB2
response.MaxWriteSize = 65536; response.MaxWriteSize = 65536;
response.SystemTime = DateTime.Now; response.SystemTime = DateTime.Now;
response.ServerStartTime = DateTime.Today; response.ServerStartTime = DateTime.Today;
response.SecurityBuffer = GSSAPIHelper.GetGSSTokenInitNTLMSSPBytes(); response.SecurityBuffer = securityProvider.GetSPNEGOTokenInitBytes();
return response; return response;
} }

View file

@ -18,20 +18,22 @@ namespace SMBLibrary.Server.SMB2
/// </summary> /// </summary>
public class SessionSetupHelper 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. // [MS-SMB2] Windows [..] will also accept raw Kerberos messages and implicit NTLM messages as part of GSS authentication.
SessionSetupResponse response = new SessionSetupResponse(); SessionSetupResponse response = new SessionSetupResponse();
byte[] messageBytes = request.SecurityBuffer; byte[] outputToken;
bool isRawMessage = true; NTStatus status = securityProvider.AcceptSecurityContext(ref state.AuthenticationContext, request.SecurityBuffer, out outputToken);
if (!AuthenticationMessageUtils.IsSignatureValid(messageBytes)) if (status != NTStatus.STATUS_SUCCESS && status != NTStatus.SEC_I_CONTINUE_NEEDED)
{ {
messageBytes = GSSAPIHelper.GetNTLMSSPMessage(request.SecurityBuffer); string userName = securityProvider.GetContextAttribute(state.AuthenticationContext, GSSAttributeName.UserName) as string;
isRawMessage = false; 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 // 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; response.Header.SessionID = sessionID.Value;
} }
MessageTypeName messageType = AuthenticationMessageUtils.GetMessageType(messageBytes); if (status == NTStatus.SEC_I_CONTINUE_NEEDED)
if (messageType == MessageTypeName.Negotiate)
{ {
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; response.Header.Status = NTStatus.STATUS_MORE_PROCESSING_REQUIRED;
} }
else // MessageTypeName.Authenticate else // status == STATUS_SUCCESS
{ {
AuthenticateMessage authenticateMessage = new AuthenticateMessage(messageBytes); string userName = securityProvider.GetContextAttribute(state.AuthenticationContext, GSSAttributeName.UserName) as string;
NTStatus loginStatus = securityProvider.Authenticate(state.AuthenticationContext, authenticateMessage); string machineName = securityProvider.GetContextAttribute(state.AuthenticationContext, GSSAttributeName.MachineName) as string;
if (loginStatus != NTStatus.STATUS_SUCCESS)
{
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?; bool? isGuest = securityProvider.GetContextAttribute(state.AuthenticationContext, GSSAttributeName.IsGuest) as bool?;
if (!isGuest.HasValue || !isGuest.Value) if (!isGuest.HasValue || !isGuest.Value)
{ {
state.LogToServer(Severity.Information, "User '{0}' authenticated successfully.", authenticateMessage.UserName); state.LogToServer(Severity.Information, "User '{0}' authenticated successfully.", userName);
state.CreateSession(request.Header.SessionID, authenticateMessage.UserName, authenticateMessage.WorkStation); state.CreateSession(request.Header.SessionID, userName, machineName);
} }
else else
{ {
state.LogToServer(Severity.Information, "User '{0}' failed authentication, logged in as guest.", authenticateMessage.UserName); state.LogToServer(Severity.Information, "User '{0}' failed authentication, logged in as guest.", userName);
state.CreateSession(request.Header.SessionID, "Guest", authenticateMessage.WorkStation); state.CreateSession(request.Header.SessionID, "Guest", machineName);
response.SessionFlags = SessionFlags.IsGuest; response.SessionFlags = SessionFlags.IsGuest;
} }
if (!isRawMessage)
{
response.SecurityBuffer = GSSAPIHelper.GetGSSTokenAcceptCompletedResponse();
}
} }
return response; return response;
} }

View file

@ -74,7 +74,7 @@ namespace SMBLibrary.Server
if (command is NegotiateRequest) if (command is NegotiateRequest)
{ {
NegotiateRequest request = (NegotiateRequest)command; 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) if (state.ServerDialect != SMBDialect.NotSet)
{ {
state = new SMB2ConnectionState(state, AllocatePersistentFileID); state = new SMB2ConnectionState(state, AllocatePersistentFileID);

View file

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

View file

@ -17,6 +17,7 @@ using System.Text;
using System.Windows.Forms; using System.Windows.Forms;
using System.Xml; using System.Xml;
using SMBLibrary; using SMBLibrary;
using SMBLibrary.Authentication.GSSAPI;
using SMBLibrary.Authentication.NTLM; using SMBLibrary.Authentication.NTLM;
using SMBLibrary.Server; using SMBLibrary.Server;
using SMBLibrary.Win32.Security; using SMBLibrary.Win32.Security;
@ -62,11 +63,10 @@ namespace SMBServer
transportType = SMBTransportType.DirectTCPTransport; transportType = SMBTransportType.DirectTCPTransport;
} }
NTLMAuthenticationProviderBase provider; NTLMAuthenticationProviderBase authenticationMechanism;
if (chkIntegratedWindowsAuthentication.Checked) if (chkIntegratedWindowsAuthentication.Checked)
{ {
provider = new IntegratedNTLMAuthenticationProvider(); authenticationMechanism = new IntegratedNTLMAuthenticationProvider();
} }
else else
{ {
@ -81,7 +81,7 @@ namespace SMBServer
return; return;
} }
provider = new IndependentNTLMAuthenticationProvider(users.GetUserPassword); authenticationMechanism = new IndependentNTLMAuthenticationProvider(users.GetUserPassword);
} }
@ -96,7 +96,8 @@ namespace SMBServer
return; 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); m_server.OnLogEntry += new EventHandler<LogEntry>(Server_OnLogEntry);
try try