diff --git a/src/libraries/Common/tests/System/Net/Security/FakeNtlmServer.cs b/src/libraries/Common/tests/System/Net/Security/FakeNtlmServer.cs index 6f576d927b2190..af19e91106cb19 100644 --- a/src/libraries/Common/tests/System/Net/Security/FakeNtlmServer.cs +++ b/src/libraries/Common/tests/System/Net/Security/FakeNtlmServer.cs @@ -37,6 +37,8 @@ public FakeNtlmServer(NetworkCredential expectedCredential) public bool TargetIsServer { get; set; } = false; public bool PreferUnicode { get; set; } = true; public bool ForceNegotiateVersion { get; set; } = true; + public bool SendPreExistingTargetName { get; set; } = false; + public bool SendPreExistingChannelBindings { get; set; } = false; // Negotiation results public bool IsAuthenticated { get; private set; } @@ -261,6 +263,21 @@ private byte[] GenerateChallenge(Flags flags) targetInfoCurrentOffset += 12; } + if (SendPreExistingTargetName) + { + // Insert a dummy TargetName AV pair that the client should replace with its own + targetInfoCurrentOffset += WriteAvIdString(buffer.AsSpan(targetInfoCurrentOffset), AvId.TargetName, "DummyTargetName"); + } + + if (SendPreExistingChannelBindings) + { + // Insert a dummy ChannelBindings AV pair (16 zero bytes) that the client should replace + BinaryPrimitives.WriteUInt16LittleEndian(buffer.AsSpan(targetInfoCurrentOffset), (ushort)AvId.ChannelBindings); + BinaryPrimitives.WriteUInt16LittleEndian(buffer.AsSpan(targetInfoCurrentOffset + 2), (ushort)16); + // Leave channel bindings hash as zeros (dummy value) + targetInfoCurrentOffset += 20; + } + // TODO: DNS machine, domain, forest? // EOL targetInfoCurrentOffset += 4; diff --git a/src/libraries/System.Net.Security/src/System/Net/NegotiateAuthenticationPal.ManagedNtlm.cs b/src/libraries/System.Net.Security/src/System/Net/NegotiateAuthenticationPal.ManagedNtlm.cs index d8909a897dbef6..497d95f06e24c6 100644 --- a/src/libraries/System.Net.Security/src/System/Net/NegotiateAuthenticationPal.ManagedNtlm.cs +++ b/src/libraries/System.Net.Security/src/System/Net/NegotiateAuthenticationPal.ManagedNtlm.cs @@ -560,7 +560,7 @@ private byte[] ProcessTargetInfo(ReadOnlySpan targetInfo, out DateTime tim return targetInfoBuffer; } - return targetInfoBuffer.AsSpan(targetInfoOffset).ToArray(); + return targetInfoBuffer.AsSpan(0, targetInfoOffset).ToArray(); } // Section 3.4.5.2 SIGNKEY, 3.4.5.3 SEALKEY diff --git a/src/libraries/System.Net.Security/tests/UnitTests/NegotiateAuthenticationTests.cs b/src/libraries/System.Net.Security/tests/UnitTests/NegotiateAuthenticationTests.cs index 016f4f63192ab6..7ccc7dd1a8d7eb 100644 --- a/src/libraries/System.Net.Security/tests/UnitTests/NegotiateAuthenticationTests.cs +++ b/src/libraries/System.Net.Security/tests/UnitTests/NegotiateAuthenticationTests.cs @@ -347,6 +347,31 @@ private void DoNtlmExchange(FakeNtlmServer fakeNtlmServer, NegotiateAuthenticati Assert.Null(empty); } + [ConditionalTheory(typeof(NegotiateAuthenticationTests), nameof(UseManagedNtlm))] + [InlineData(true, false)] + [InlineData(false, true)] + [InlineData(true, true)] + public void NtlmWithPreExistingTargetInfoEntriesTest(bool sendPreExistingTargetName, bool sendPreExistingChannelBindings) + { + using FakeNtlmServer fakeNtlmServer = new FakeNtlmServer(s_testCredentialRight) + { + SendPreExistingTargetName = sendPreExistingTargetName, + SendPreExistingChannelBindings = sendPreExistingChannelBindings, + }; + NegotiateAuthentication ntAuth = new NegotiateAuthentication( + new NegotiateAuthenticationClientOptions + { + Package = "NTLM", + Credential = s_testCredentialRight, + TargetName = "HTTP/foo", + RequiredProtectionLevel = ProtectionLevel.Sign + }); + + DoNtlmExchange(fakeNtlmServer, ntAuth); + + Assert.True(fakeNtlmServer.IsAuthenticated); + } + [ConditionalTheory(typeof(NegotiateAuthenticationTests), nameof(IsNtlmAvailable))] [InlineData(true, true)] [InlineData(true, false)]