From 2bda1010d1f469897c8a50a7f85da4217f5b9ed5 Mon Sep 17 00:00:00 2001 From: Meik Date: Wed, 18 Mar 2026 13:48:24 +0100 Subject: [PATCH] Harden NTFS SMB login retry --- LiamNtfs/cNtfsBase.cs | 71 ++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 66 insertions(+), 5 deletions(-) diff --git a/LiamNtfs/cNtfsBase.cs b/LiamNtfs/cNtfsBase.cs index 0feb229..f19b13a 100644 --- a/LiamNtfs/cNtfsBase.cs +++ b/LiamNtfs/cNtfsBase.cs @@ -17,6 +17,9 @@ namespace LiamNtfs { public class cNtfsBase { + private const int ErrorSessionCredentialConflict = 1219; + private const int ErrorNotConnected = 2250; + private cNtfsLogonInfo privLogonInfo = null; private int scanningDepth; public PrincipalContext adContext = null; @@ -58,15 +61,40 @@ namespace LiamNtfs LogonInfo.UserSecret, LogonInfo.User, 0); - if(result == 1219) + + if (result == ErrorSessionCredentialConflict) { - result = WNetCancelConnection2(LogonInfo.TargetNetworkName,0,true); - if (result == 0) - return await privLogonAsync(LogonInfo); + var originalResult = result; + var cancelResult = WNetCancelConnection2(LogonInfo.TargetNetworkName, 0, true); + + if (cancelResult == 0 || cancelResult == ErrorNotConnected) + { + result = WNetAddConnection2( + netResource, + LogonInfo.UserSecret, + LogonInfo.User, + 0); + + if (result == 0) + { + cLogManager.LogEntry( + $"NTFS login retry succeeded after SMB conflict on '{LogonInfo.TargetNetworkName}'. Initial add returned {originalResult} ({BuildWin32Message(originalResult)}), cancel returned {cancelResult} ({BuildWin32Message(cancelResult)}).", + LogLevels.Debug); + } + else + { + throw CreateNtfsLoginException(LogonInfo, originalResult, cancelResult, result); + } + } + else + { + throw CreateNtfsLoginException(LogonInfo, originalResult, cancelResult, null); + } } + if (result != 0) { - throw new Win32Exception(result); + throw CreateNtfsLoginException(LogonInfo, result, null, null); } var FSLogon = true; @@ -83,6 +111,39 @@ namespace LiamNtfs return false; } + private static Exception CreateNtfsLoginException(cNtfsLogonInfo logonInfo, int addResult, int? cancelResult, int? retryResult) + { + var stages = new List + { + $"WNetAddConnection2={addResult} ({BuildWin32Message(addResult)})" + }; + + if (cancelResult.HasValue) + stages.Add($"WNetCancelConnection2('{logonInfo?.TargetNetworkName}')={cancelResult.Value} ({BuildWin32Message(cancelResult.Value)})"); + + if (retryResult.HasValue) + stages.Add($"RetryAdd={retryResult.Value} ({BuildWin32Message(retryResult.Value)})"); + + var details = string.Join(", ", stages); + var message = $"NTFS login to '{logonInfo?.TargetNetworkName}' for user '{logonInfo?.User}' failed. {details}."; + + if (addResult == ErrorSessionCredentialConflict) + { + message += " This usually indicates an existing SMB/DFS session to the same server with a different credential context."; + + if (cancelResult == ErrorNotConnected) + message += " The exact DFS/share path was not registered as a disconnectable connection, so the conflicting session is likely held under a different remote name or DFS target."; + } + + var nativeResult = retryResult ?? cancelResult ?? addResult; + return new InvalidOperationException(message, new Win32Exception(nativeResult)); + } + + private static string BuildWin32Message(int errorCode) + { + return new Win32Exception(errorCode).Message; + } + private async Task privRelogon() { if (privLogonInfo == null)