public void NtlmSignatureTest() { FakeNtlmServer fakeNtlmServer = new FakeNtlmServer(s_testCredentialRight); NTAuthentication ntAuth = new NTAuthentication( isServer: false, "NTLM", s_testCredentialRight, "HTTP/foo", ContextFlagsPal.Connection | ContextFlagsPal.InitIntegrity | ContextFlagsPal.Confidentiality, null); DoNtlmExchange(fakeNtlmServer, ntAuth); Assert.True(fakeNtlmServer.IsAuthenticated); // Test MakeSignature on client side and decoding it on server side byte[]? output = null; int len = ntAuth.MakeSignature(s_Hello, 0, s_Hello.Length, ref output); Assert.NotNull(output); Assert.Equal(16 + s_Hello.Length, len); // Unseal the content and check it byte[] temp = new byte[s_Hello.Length]; fakeNtlmServer.Unseal(output.AsSpan(16), temp); Assert.Equal(s_Hello, temp); // Check the signature fakeNtlmServer.VerifyMIC(temp, output.AsSpan(0, 16), sequenceNumber: 0); // Test creating signature on server side and decoding it with VerifySignature on client side byte[] serverSignedMessage = new byte[16 + s_Hello.Length]; fakeNtlmServer.Seal(s_Hello, serverSignedMessage.AsSpan(16, s_Hello.Length)); fakeNtlmServer.GetMIC(s_Hello, serverSignedMessage.AsSpan(0, 16), sequenceNumber: 0); len = ntAuth.VerifySignature(serverSignedMessage, 0, serverSignedMessage.Length); Assert.Equal(s_Hello.Length, len); // NOTE: VerifySignature doesn't return the content on Windows // Assert.Equal(s_Hello, serverSignedMessage.AsSpan(0, len).ToArray()); }
public void NtlmIncorrectExchangeTest() { FakeNtlmServer fakeNtlmServer = new FakeNtlmServer(s_testCredentialRight); NTAuthentication ntAuth = new NTAuthentication( isServer: false, "NTLM", s_testCredentialWrong, "HTTP/foo", ContextFlagsPal.Connection | ContextFlagsPal.InitIntegrity, null); DoNtlmExchange(fakeNtlmServer, ntAuth); Assert.False(fakeNtlmServer.IsAuthenticated); }
public void NtlmProtocolExampleTest() { // Mirrors the NTLMv2 example in the NTLM specification: NetworkCredential credential = new NetworkCredential("User", "Password", "Domain"); FakeNtlmServer fakeNtlmServer = new FakeNtlmServer(credential); fakeNtlmServer.SendTimestamp = false; fakeNtlmServer.TargetIsServer = true; fakeNtlmServer.PreferUnicode = false; // NEGOTIATE_MESSAGE // Flags: // NTLMSSP_NEGOTIATE_KEY_EXCH // NTLMSSP_NEGOTIATE_56 // NTLMSSP_NEGOTIATE_128 // NTLMSSP_NEGOTIATE_VERSION // NTLMSSP_NEGOTIATE_TARGET_INFO // NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY // NTLMSSP_TARGET_TYPE_SERVER // NTLMSSP_NEGOTIATE_ALWAYS_SIGN // NTLMSSP_NEGOTIATE_NTLM // NTLMSSP_NEGOTIATE_SEAL // NTLMSSP_NEGOTIATE_SIGN // NTLMSSP_NEGOTIATE_OEM // NTLMSSP_NEGOTIATE_UNICODE // Domain: (empty) (should be "Domain" but the fake server doesn't check) // Workstation: (empty) (should be "COMPUTER" but the fake server doesn't check) // Version: 6.1.7600 / 15 byte[] negotiateBlob = Convert.FromHexString("4e544c4d535350000100000033828ae2000000000000000000000000000000000601b01d0000000f"); byte[]? challengeBlob = fakeNtlmServer.GetOutgoingBlob(negotiateBlob); // CHALLENGE_MESSAGE from 4.2.4.3 Messages byte[] expectedChallengeBlob = Convert.FromHexString( "4e544c4d53535000020000000c000c003800000033828ae20123456789abcdef" + "00000000000000002400240044000000060070170000000f5300650072007600" + "6500720002000c0044006f006d00610069006e0001000c005300650072007600" + "6500720000000000"); Assert.Equal(expectedChallengeBlob, challengeBlob); // AUTHENTICATE_MESSAGE from 4.2.4.3 Messages byte[] authenticateBlob = Convert.FromHexString( "4e544c4d5353500003000000180018006c00000054005400840000000c000c00" + "480000000800080054000000100010005c00000010001000d8000000358288e2" + "0501280a0000000f44006f006d00610069006e00550073006500720043004f00" + "4d005000550054004500520086c35097ac9cec102554764a57cccc19aaaaaaaa" + "aaaaaaaa68cd0ab851e51c96aabc927bebef6a1c010100000000000000000000" + "00000000aaaaaaaaaaaaaaaa0000000002000c0044006f006d00610069006e00" + "01000c005300650072007600650072000000000000000000c5dad2544fc97990" + "94ce1ce90bc9d03e"); byte[]? empty = fakeNtlmServer.GetOutgoingBlob(authenticateBlob); Assert.Null(empty); Assert.True(fakeNtlmServer.IsAuthenticated); Assert.False(fakeNtlmServer.IsMICPresent); }
private void DoNtlmExchange(FakeNtlmServer fakeNtlmServer, NTAuthentication ntAuth) { byte[]? negotiateBlob = ntAuth.GetOutgoingBlob(null, throwOnError: false); Assert.NotNull(negotiateBlob); byte[]? challengeBlob = fakeNtlmServer.GetOutgoingBlob(negotiateBlob); Assert.NotNull(challengeBlob); byte[]? authenticateBlob = ntAuth.GetOutgoingBlob(challengeBlob, throwOnError: false); Assert.NotNull(authenticateBlob); byte[]? empty = fakeNtlmServer.GetOutgoingBlob(authenticateBlob); Assert.Null(empty); }
public void NtlmCorrectExchangeTest() { FakeNtlmServer fakeNtlmServer = new FakeNtlmServer(s_testCredentialRight); NTAuthentication ntAuth = new NTAuthentication( isServer: false, "NTLM", s_testCredentialRight, "HTTP/foo", ContextFlagsPal.Connection | ContextFlagsPal.InitIntegrity, null); DoNtlmExchange(fakeNtlmServer, ntAuth); Assert.True(fakeNtlmServer.IsAuthenticated); // NTLMSSP on Linux doesn't send the MIC and sends incorrect SPN (drops the service prefix) if (!OperatingSystem.IsLinux()) { Assert.True(fakeNtlmServer.IsMICPresent); Assert.Equal("HTTP/foo", fakeNtlmServer.ClientSpecifiedSpn); } }
internal static async Task HandleAuthenticationRequestWithFakeServer(LoopbackServer.Connection connection, bool useNtlm) { HttpRequestData request = await connection.ReadRequestDataAsync(); FakeNtlmServer? fakeNtlmServer = null; FakeNegotiateServer?fakeNegotiateServer = null; string authHeader = null; foreach (HttpHeaderData header in request.Headers) { if (header.Name == "Authorization") { authHeader = header.Value; break; } } if (string.IsNullOrEmpty(authHeader)) { // This is initial request, we reject with showing supported mechanisms. if (useNtlm) { authHeader = "WWW-Authenticate: NTLM\r\n"; } else { authHeader = "WWW-Authenticate: Negotiate\r\n"; } await connection.SendResponseAsync(HttpStatusCode.Unauthorized, authHeader).ConfigureAwait(false); connection.CompleteRequestProcessing(); // Read next requests and fall-back to loop bellow to process it. request = await connection.ReadRequestDataAsync(); } bool isAuthenticated = false; do { foreach (HttpHeaderData header in request.Headers) { if (header.Name == "Authorization") { authHeader = header.Value; break; } } Assert.NotNull(authHeader); var tokens = authHeader.Split(' ', 2, StringSplitOptions.TrimEntries); // Should be type and base64 encoded blob Assert.Equal(2, tokens.Length); if (fakeNtlmServer == null) { fakeNtlmServer = new FakeNtlmServer(s_testCredentialRight) { ForceNegotiateVersion = true }; if (!useNtlm) { fakeNegotiateServer = new FakeNegotiateServer(fakeNtlmServer); } } byte[]? outBlob; if (fakeNegotiateServer != null) { outBlob = fakeNegotiateServer.GetOutgoingBlob(Convert.FromBase64String(tokens[1])); isAuthenticated = fakeNegotiateServer.IsAuthenticated; } else { outBlob = fakeNtlmServer.GetOutgoingBlob(Convert.FromBase64String(tokens[1])); isAuthenticated = fakeNtlmServer.IsAuthenticated; } if (outBlob != null) { authHeader = $"WWW-Authenticate: {tokens[0]} {Convert.ToBase64String(outBlob)}\r\n"; await connection.SendResponseAsync(isAuthenticated?HttpStatusCode.OK : HttpStatusCode.Unauthorized, authHeader); connection.CompleteRequestProcessing(); if (!isAuthenticated) { request = await connection.ReadRequestDataAsync(); } } }while (!isAuthenticated); await connection.SendResponseAsync(HttpStatusCode.OK); }