diff --git a/eng/versioning.targets b/eng/versioning.targets index 46bf2743bbd82f..baa004f690cc11 100644 --- a/eng/versioning.targets +++ b/eng/versioning.targets @@ -81,6 +81,7 @@ + diff --git a/src/libraries/System.Net.Security/src/System.Net.Security.csproj b/src/libraries/System.Net.Security/src/System.Net.Security.csproj index 577bcccf801c5d..0161ddcee2447f 100644 --- a/src/libraries/System.Net.Security/src/System.Net.Security.csproj +++ b/src/libraries/System.Net.Security/src/System.Net.Security.csproj @@ -1,7 +1,7 @@ - $(NetCoreAppCurrent)-windows;$(NetCoreAppCurrent)-linux;$(NetCoreAppCurrent)-freebsd;$(NetCoreAppCurrent)-haiku;$(NetCoreAppCurrent)-android;$(NetCoreAppCurrent)-osx;$(NetCoreAppCurrent)-ios;$(NetCoreAppCurrent)-tvos;$(NetCoreAppCurrent) + $(NetCoreAppCurrent)-windows;$(NetCoreAppCurrent)-linux;$(NetCoreAppCurrent)-freebsd;$(NetCoreAppCurrent)-openbsd;$(NetCoreAppCurrent)-haiku;$(NetCoreAppCurrent)-android;$(NetCoreAppCurrent)-osx;$(NetCoreAppCurrent)-ios;$(NetCoreAppCurrent)-tvos;$(NetCoreAppCurrent) true $(DefineConstants);PRODUCT diff --git a/src/libraries/System.Net.Security/src/System/Net/NegotiateAuthenticationPal.ManagedSpnego.cs b/src/libraries/System.Net.Security/src/System/Net/NegotiateAuthenticationPal.ManagedSpnego.cs index ad860ec7595f5b..56570e546beb62 100644 --- a/src/libraries/System.Net.Security/src/System/Net/NegotiateAuthenticationPal.ManagedSpnego.cs +++ b/src/libraries/System.Net.Security/src/System/Net/NegotiateAuthenticationPal.ManagedSpnego.cs @@ -182,7 +182,18 @@ private IEnumerable> EnumerateMechanisms() mechBlob = null; _optimisticMechanism?.Dispose(); _optimisticMechanism = null; - if (statusCode != NegotiateAuthenticationStatusCode.Unsupported) + + // When an optimistic mechanism (e.g. Kerberos) is unavailable, fall + // back to the next offered mechanism (NTLM) instead of failing the + // whole exchange. NTLM is always the last mechanism, so a fallback is + // only attempted when another mechanism follows. On OpenBSD the GSS-API + // (Heimdal) reports a missing Kerberos TGT as UnknownCredentials rather + // than Unsupported, so that status is also treated as recoverable there. + bool canFallBackToNextMechanism = packageAndOid.Key != NegotiationInfoClass.NTLM; + bool isRecoverableOptimisticFailure = + statusCode == NegotiateAuthenticationStatusCode.Unsupported || + (LocalAppContextSwitches.IsOpenBsd && statusCode == NegotiateAuthenticationStatusCode.UnknownCredentials); + if (!canFallBackToNextMechanism || !isRecoverableOptimisticFailure) { return null; } diff --git a/src/libraries/System.Net.Security/src/System/Net/Security/LocalAppContextSwitches.cs b/src/libraries/System.Net.Security/src/System/Net/Security/LocalAppContextSwitches.cs index 369ece8ca60d0e..e864f6355f427b 100644 --- a/src/libraries/System.Net.Security/src/System/Net/Security/LocalAppContextSwitches.cs +++ b/src/libraries/System.Net.Security/src/System/Net/Security/LocalAppContextSwitches.cs @@ -9,6 +9,10 @@ namespace System { internal static partial class LocalAppContextSwitches { + // OpenBSD's GSS-API (Heimdal) cannot drive password-based NTLM and reports a + // missing Kerberos TGT as a missing credential, so managed NTLM is used there. + internal static readonly bool IsOpenBsd = RuntimeInformation.IsOSPlatform(OSPlatform.Create("OPENBSD")); + private static int s_disableTlsResume; internal static bool DisableTlsResume { @@ -38,6 +42,7 @@ internal static bool UseManagedNtlm [MethodImpl(MethodImplOptions.AggressiveInlining)] get => GetCachedSwitchValue("System.Net.Security.UseManagedNtlm", ref s_useManagedNtlm, defaultValue: OperatingSystem.IsMacOS() || OperatingSystem.IsIOS() || OperatingSystem.IsMacCatalyst() || + IsOpenBsd || (OperatingSystem.IsLinux() && RuntimeInformation.RuntimeIdentifier.StartsWith("linux-bionic-", StringComparison.OrdinalIgnoreCase))); } #endif diff --git a/src/libraries/System.Net.Security/tests/FunctionalTests/TestConfiguration.cs b/src/libraries/System.Net.Security/tests/FunctionalTests/TestConfiguration.cs index 9cdc176c7d1a7b..c59da067c869b1 100644 --- a/src/libraries/System.Net.Security/tests/FunctionalTests/TestConfiguration.cs +++ b/src/libraries/System.Net.Security/tests/FunctionalTests/TestConfiguration.cs @@ -26,8 +26,8 @@ internal static class TestConfiguration public const string NtlmUserFilePath = "/var/tmp/ntlm_user_file"; public static bool SupportsNullEncryption { get { return s_supportsNullEncryption.Value; } } - public static bool SupportsHandshakeAlerts { get { return OperatingSystem.IsLinux() || OperatingSystem.IsWindows() || OperatingSystem.IsFreeBSD(); } } - public static bool SupportsRenegotiation { get { return OperatingSystem.IsWindows() || ((OperatingSystem.IsLinux() || OperatingSystem.IsFreeBSD()) && PlatformDetection.OpenSslVersion >= new Version(1, 1, 1)); } } + public static bool SupportsHandshakeAlerts { get { return OperatingSystem.IsLinux() || OperatingSystem.IsWindows() || OperatingSystem.IsFreeBSD() || RuntimeInformation.IsOSPlatform(OSPlatform.Create("OPENBSD")); } } + public static bool SupportsRenegotiation { get { return OperatingSystem.IsWindows() || ((OperatingSystem.IsLinux() || OperatingSystem.IsFreeBSD() || RuntimeInformation.IsOSPlatform(OSPlatform.Create("OPENBSD"))) && PlatformDetection.OpenSslVersion >= new Version(1, 1, 1)); } } public static readonly X509Certificate2 ServerCertificate = System.Net.Test.Common.Configuration.Certificates.GetServerCertificate();