mirror of
https://github.com/TalAloni/SMBLibrary.git
synced 2025-04-30 10:47:48 +02:00
GSS-style authentication, additional IGSSMechanism implementations can be provided
This commit is contained in:
parent
d4acf5900e
commit
a84226abb9
11 changed files with 275 additions and 182 deletions
|
@ -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
SMBLibrary/Authentication/GSSAPI/GSSProvider.cs
Normal file
213
SMBLibrary/Authentication/GSSAPI/GSSProvider.cs
Normal 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();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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,
|
||||
|
|
|
@ -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" />
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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,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"
|
||||
|
|
|
@ -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,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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
{
|
||||
|
@ -81,7 +81,7 @@ namespace SMBServer
|
|||
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
|
||||
|
|
Loading…
Add table
Reference in a new issue