From 4c6494172a65be36e1cd6b5f40084e9481f924cc Mon Sep 17 00:00:00 2001 From: Jacob Hales Date: Sat, 18 Apr 2026 14:08:32 -0600 Subject: [PATCH] feat(auth)!: add ResetSecurityContext to IAuthenticationClient for DFS target auth Add ResetSecurityContext(string host) to IAuthenticationClient interface, enabling auth-client reuse when connecting to DFS target servers. - IAuthenticationClient: add ResetSecurityContext(string host) method - NTLMAuthenticationClient: implement reset (update SPN, clear handshake state) - SMB2Client: retain IAuthenticationClient after Login, clear on Logoff/Disconnect - SMB2Client: expose Transport property for target client construction BREAKING CHANGE: IAuthenticationClient now requires a ResetSecurityContext(string host) method. Existing implementations must add this method. --- .../Authentication/IAuthenticationClient.cs | 2 ++ .../Authentication/NTLMAuthenticationClient.cs | 8 ++++++++ SMBLibrary/Client/SMB2Client.cs | 15 +++++++++++++++ 3 files changed, 25 insertions(+) diff --git a/SMBLibrary/Client/Authentication/IAuthenticationClient.cs b/SMBLibrary/Client/Authentication/IAuthenticationClient.cs index fe9115cb..c62df3cc 100644 --- a/SMBLibrary/Client/Authentication/IAuthenticationClient.cs +++ b/SMBLibrary/Client/Authentication/IAuthenticationClient.cs @@ -12,5 +12,7 @@ public interface IAuthenticationClient byte[] InitializeSecurityContext(byte[] securityBlob); byte[] GetSessionKey(); + + void ResetSecurityContext(string host); } } diff --git a/SMBLibrary/Client/Authentication/NTLMAuthenticationClient.cs b/SMBLibrary/Client/Authentication/NTLMAuthenticationClient.cs index d6ca8531..51491185 100644 --- a/SMBLibrary/Client/Authentication/NTLMAuthenticationClient.cs +++ b/SMBLibrary/Client/Authentication/NTLMAuthenticationClient.cs @@ -130,6 +130,14 @@ public virtual byte[] GetSessionKey() return m_sessionKey; } + public virtual void ResetSecurityContext(string host) + { + m_spn = string.Format("cifs/{0}", host); + m_isNegotiationMessageAcquired = false; + m_negotiateMessageBytes = null; + m_sessionKey = null; + } + private static bool ContainsMechanism(SimpleProtectedNegotiationTokenInit token, byte[] mechanismIdentifier) { for (int index = 0; index < token.MechanismTypeList.Count; index++) diff --git a/SMBLibrary/Client/SMB2Client.cs b/SMBLibrary/Client/SMB2Client.cs index 5ff53646..34e1cd3b 100644 --- a/SMBLibrary/Client/SMB2Client.cs +++ b/SMBLibrary/Client/SMB2Client.cs @@ -60,6 +60,7 @@ public class SMB2Client : ISMBClient private byte[] m_preauthIntegrityHashValue; // SMB 3.1.1 private ushort m_availableCredits = 1; private bool m_connectionSupportsMultiCredit = false; + private IAuthenticationClient m_authenticationClient; public SMB2Client() : this(DefaultResponseTimeoutInMilliseconds) { @@ -202,6 +203,7 @@ public void Disconnect() m_sessionID = 0; m_availableCredits = 1; m_connectionSupportsMultiCredit = false; + m_authenticationClient = null; } } @@ -298,6 +300,7 @@ public NTStatus Login(IAuthenticationClient authenticationClient) { m_sessionID = response.Header.SessionID; m_sessionKey = authenticationClient.GetSessionKey(); + m_authenticationClient = authenticationClient; SessionFlags sessionFlags = finalSessionSetupResponse.SessionFlags; if ((sessionFlags & SessionFlags.IsGuest) > 0) { @@ -339,6 +342,10 @@ public NTStatus Logoff() if (response != null) { m_isLoggedIn = (response.Header.Status != NTStatus.STATUS_SUCCESS); + if (!m_isLoggedIn) + { + m_authenticationClient = null; + } return response.Header.Status; } return NTStatus.STATUS_INVALID_SMB; @@ -770,6 +777,14 @@ public bool IsConnected } } + public SMBTransportType Transport + { + get + { + return m_transport; + } + } + private void TrySendCommand(Socket socket, SMB2Command request, byte[] encryptionKey) { SessionMessagePacket packet = new SessionMessagePacket();