From 69fc45754549ddcb596f367e3dda615800281e63 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 5 Mar 2026 02:50:17 +0000 Subject: [PATCH 1/3] Initial plan From 22758741cb82176caff41b608bbf8a69125bba2b Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 5 Mar 2026 03:14:36 +0000 Subject: [PATCH 2/3] Fix NTLM ProcessTargetInfo returning wrong buffer slice; add test coverage Co-authored-by: stephentoub <2642209+stephentoub@users.noreply.github.com> --- .../System/Net/Security/FakeNtlmServer.cs | 17 +++++++++++++ .../NegotiateAuthenticationPal.ManagedNtlm.cs | 2 +- .../UnitTests/NegotiateAuthenticationTests.cs | 25 +++++++++++++++++++ 3 files changed, 43 insertions(+), 1 deletion(-) 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..d50ffb58c03aa2 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(IsNtlmAvailable))] + [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)] From 47f7485c8ede10c1ad703f881206f1ff60821b5a Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 6 Mar 2026 08:22:31 +0000 Subject: [PATCH 3/3] Fix test condition: gate NtlmWithPreExistingTargetInfoEntriesTest on UseManagedNtlm Co-authored-by: rzikm <32671551+rzikm@users.noreply.github.com> --- .../tests/UnitTests/NegotiateAuthenticationTests.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libraries/System.Net.Security/tests/UnitTests/NegotiateAuthenticationTests.cs b/src/libraries/System.Net.Security/tests/UnitTests/NegotiateAuthenticationTests.cs index d50ffb58c03aa2..7ccc7dd1a8d7eb 100644 --- a/src/libraries/System.Net.Security/tests/UnitTests/NegotiateAuthenticationTests.cs +++ b/src/libraries/System.Net.Security/tests/UnitTests/NegotiateAuthenticationTests.cs @@ -347,7 +347,7 @@ private void DoNtlmExchange(FakeNtlmServer fakeNtlmServer, NegotiateAuthenticati Assert.Null(empty); } - [ConditionalTheory(typeof(NegotiateAuthenticationTests), nameof(IsNtlmAvailable))] + [ConditionalTheory(typeof(NegotiateAuthenticationTests), nameof(UseManagedNtlm))] [InlineData(true, false)] [InlineData(false, true)] [InlineData(true, true)]