Пример #1
0
 public async Task UseClientCertOnHttp2_DowngradedToHttp1MutualAuth_Success()
 {
     using X509Certificate2 clientCert = Test.Common.Configuration.Certificates.GetClientCertificate();
     await LoopbackServer.CreateClientAndServerAsync(
         async address =>
         {
             var handler = new WinHttpHandler();
             handler.ServerCertificateValidationCallback = CustomServerCertificateValidationCallback;
             handler.ClientCertificates.Add(clientCert);
             handler.ClientCertificateOption = ClientCertificateOption.Manual;
             using (var client = new HttpClient(handler))
             using (HttpResponseMessage response = await client.SendAsync(new HttpRequestMessage(HttpMethod.Get, address) { Version = HttpVersion20.Value }))
             {
                 Assert.Equal(HttpStatusCode.OK, response.StatusCode);
                 Assert.True(_validationCallbackHistory.WasCalled);
                 Assert.NotEmpty(_validationCallbackHistory.CertificateChain);
                 Assert.Equal(Test.Common.Configuration.Certificates.GetServerCertificate(), _validationCallbackHistory.CertificateChain[0]);
             }
         },
         async s =>
         {
             await using (LoopbackServer.Connection connection = await s.EstablishConnectionAsync().ConfigureAwait(false))
             {
                 SslStream sslStream = connection.Stream as SslStream;
                 Assert.NotNull(sslStream);
                 Assert.True(sslStream.IsMutuallyAuthenticated);
                 Assert.Equal(clientCert, sslStream.RemoteCertificate);
                 await connection.ReadRequestHeaderAndSendResponseAsync(HttpStatusCode.OK);
             }
         }, new LoopbackServer.Options { UseSsl = true });
 }
Пример #2
0
        public static async Task <bool> WebSocketHandshakeAsync(LoopbackServer.Connection connection)
        {
            string serverResponse = null;
            string currentRequestLine;

            while (!string.IsNullOrEmpty(currentRequestLine = await connection.Reader.ReadLineAsync().ConfigureAwait(false)))
            {
                string[] tokens = currentRequestLine.Split(new char[] { ':' }, 2);
                if (tokens.Length == 2)
                {
                    string headerName = tokens[0];
                    if (headerName == "Sec-WebSocket-Key")
                    {
                        string headerValue = tokens[1].Trim();
                        string responseSecurityAcceptValue = ComputeWebSocketHandshakeSecurityAcceptValue(headerValue);
                        serverResponse =
                            "HTTP/1.1 101 Switching Protocols\r\n" +
                            "Upgrade: websocket\r\n" +
                            "Connection: Upgrade\r\n" +
                            "Sec-WebSocket-Accept: " + responseSecurityAcceptValue + "\r\n\r\n";
                    }
                }
            }

            if (serverResponse != null)
            {
                // We received a valid WebSocket opening handshake. Send the appropriate response.
                await connection.Writer.WriteAsync(serverResponse).ConfigureAwait(false);

                return(true);
            }

            return(false);
        }
        public async Task Credentials_BrokenNtlmFromServer()
        {
            if (IsWinHttpHandler && UseVersion >= HttpVersion20.Value)
            {
                return;
            }

            await LoopbackServer.CreateClientAndServerAsync(
                async uri =>
            {
                using (HttpClientHandler handler = CreateHttpClientHandler())
                    using (HttpClient client = CreateHttpClient(handler))
                    {
                        handler.Credentials = new NetworkCredential("username", "password");
                        var response        = await client.GetAsync(uri);
                        Assert.Equal(HttpStatusCode.Unauthorized, response.StatusCode);
                    }
            },
                async server =>
            {
                var responseHeader          = new HttpHeaderData[] { new HttpHeaderData("WWW-Authenticate", "NTLM") };
                HttpRequestData requestData = await server.HandleRequestAsync(HttpStatusCode.Unauthorized, responseHeader);
                Assert.Equal(0, requestData.GetHeaderValueCount("Authorization"));

                // Establish a session connection
                await using LoopbackServer.Connection connection = await server.EstablishConnectionAsync();
                requestData            = await connection.ReadRequestDataAsync();
                string authHeaderValue = requestData.GetSingleHeaderValue("Authorization");
                Assert.Contains("NTLM", authHeaderValue);
                _output.WriteLine(authHeaderValue);

                // Incorrect NTLMv1 challenge from server (generated by Cyrus HTTP)
                responseHeader = new HttpHeaderData[] {
                    new HttpHeaderData("WWW-Authenticate", "NTLM TlRMTVNTUAACAAAAHAAcADAAAACV/wIAUwCrhitz1vsAAAAAAAAAAAAAAAAAAAAASgAuAEUATQBDAEwASQBFAE4AVAAuAEMATwBNAA=="),
                    new HttpHeaderData("Connection", "keep-alive")
                };
                await connection.SendResponseAsync(HttpStatusCode.Unauthorized, responseHeader);
                connection.CompleteRequestProcessing();

                // Wait for the client to close the connection
                try
                {
                    CancellationTokenSource cancellationTokenSource = new CancellationTokenSource(1000);
                    await connection.WaitForCloseAsync(cancellationTokenSource.Token);
                }
                catch (OperationCanceledException)
                {
                    // On Linux the GSSAPI NTLM provider may try to continue with the authentication, so go along with it
                    requestData     = await connection.ReadRequestDataAsync();
                    authHeaderValue = requestData.GetSingleHeaderValue("Authorization");
                    Assert.Contains("NTLM", authHeaderValue);
                    _output.WriteLine(authHeaderValue);
                    await connection.SendResponseAsync(HttpStatusCode.Unauthorized);
                    connection.CompleteRequestProcessing();
                }
            });
        }
Пример #4
0
        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);
        }
Пример #5
0
        internal static async Task HandleAuthenticationRequest(LoopbackServer.Connection connection, bool useNtlm, bool useNegotiate, bool closeConnection)
        {
            HttpRequestData request = await connection.ReadRequestDataAsync();

            NTAuthentication authContext = 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.
                authHeader = string.Empty;
                if (useNtlm)
                {
                    authHeader += NtlmAuthHeader + "\r\n";
                }

                if (useNegotiate)
                {
                    authHeader += NegotiateAuthHeader + "\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();
            }

            SecurityStatusPal statusCode;

            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);

                authContext ??= new NTAuthentication(isServer: true, tokens[0], CredentialCache.DefaultNetworkCredentials, null, ContextFlagsPal.Connection, null);

                byte[]? outBlob = authContext.GetOutgoingBlob(Convert.FromBase64String(tokens[1]), throwOnError: false, out statusCode);

                if (outBlob != null && statusCode.ErrorCode == SecurityStatusPalErrorCode.ContinueNeeded)
                {
                    authHeader = $"WWW-Authenticate: {tokens[0]} {Convert.ToBase64String(outBlob)}\r\n";
                    await connection.SendResponseAsync(HttpStatusCode.Unauthorized, authHeader);

                    connection.CompleteRequestProcessing();

                    request = await connection.ReadRequestDataAsync();
                }
            }while (statusCode.ErrorCode == SecurityStatusPalErrorCode.ContinueNeeded);

            if (statusCode.ErrorCode == SecurityStatusPalErrorCode.OK)
            {
                // If authentication succeeded ask Windows about the identity and send it back as custom header.
                SecurityContextTokenHandle?userContext = null;
                using SafeDeleteContext securityContext = authContext.GetContext(out SecurityStatusPal statusCodeNew) !;
                SSPIWrapper.QuerySecurityContextToken(GlobalSSPI.SSPIAuth, securityContext, out userContext);
                using WindowsIdentity identity = new WindowsIdentity(userContext.DangerousGetHandle(), authContext.ProtocolName);

                authHeader = $"{UserHeaderName}: {identity.Name}\r\n";
                if (closeConnection)
                {
                    authHeader += "Connection: close\r\n";
                }

                await connection.SendResponseAsync(HttpStatusCode.OK, authHeader, "foo");

                userContext.Dispose();
            }
            else
            {
                await connection.SendResponseAsync(HttpStatusCode.Forbidden, "Connection: close\r\n", "boo");
            }
        }
Пример #6
0
 internal static Task HandleNegotiateAuthenticationRequest(LoopbackServer.Connection connection, bool closeConnection = true)
 {
     return(HandleAuthenticationRequest(connection, useNtlm: false, useNegotiate: true, closeConnection));
 }
Пример #7
0
        public static async Task <Dictionary <string, string> > WebSocketHandshakeAsync(LoopbackServer.Connection connection)
        {
            string        serverResponse = null;
            List <string> headers        = await connection.ReadRequestHeaderAsync().ConfigureAwait(false);

            var results = new Dictionary <string, string>();

            foreach (string header in headers)
            {
                string[] tokens = header.Split(new char[] { ':' }, StringSplitOptions.RemoveEmptyEntries);
                if (tokens.Length == 2)
                {
                    results.Add(tokens[0].Trim(), tokens[1].Trim());

                    string headerName = tokens[0];
                    if (headerName == "Sec-WebSocket-Key")
                    {
                        string headerValue = tokens[1].Trim();
                        string responseSecurityAcceptValue = ComputeWebSocketHandshakeSecurityAcceptValue(headerValue);
                        serverResponse =
                            "HTTP/1.1 101 Switching Protocols\r\n" +
                            "Content-Length: 0\r\n" +
                            "Upgrade: websocket\r\n" +
                            "Connection: Upgrade\r\n" +
                            "Sec-WebSocket-Accept: " + responseSecurityAcceptValue + "\r\n\r\n";
                    }
                }
            }

            if (serverResponse != null)
            {
                // We received a valid WebSocket opening handshake. Send the appropriate response.
                await connection.Writer.WriteAsync(serverResponse).ConfigureAwait(false);

                return(results);
            }

            return(null);
        }
Пример #8
0
        internal static async Task HandleAuthenticationRequest(LoopbackServer.Connection connection, bool useNtlm, bool useNegotiate, bool closeConnection)
        {
            HttpRequestData request = await connection.ReadRequestDataAsync();

            NegotiateAuthentication authContext = 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.
                authHeader = string.Empty;
                if (useNtlm)
                {
                    authHeader += NtlmAuthHeader + "\r\n";
                }

                if (useNegotiate)
                {
                    authHeader += NegotiateAuthHeader + "\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();
            }

            NegotiateAuthenticationStatusCode statusCode;

            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);

                authContext ??= new NegotiateAuthentication(new NegotiateAuthenticationServerOptions {
                    Package = tokens[0]
                });

                byte[]? outBlob = authContext.GetOutgoingBlob(Convert.FromBase64String(tokens[1]), out statusCode);

                if (outBlob != null && statusCode == NegotiateAuthenticationStatusCode.ContinueNeeded)
                {
                    authHeader = $"WWW-Authenticate: {tokens[0]} {Convert.ToBase64String(outBlob)}\r\n";
                    await connection.SendResponseAsync(HttpStatusCode.Unauthorized, authHeader);

                    connection.CompleteRequestProcessing();

                    request = await connection.ReadRequestDataAsync();
                }
            }while (statusCode == NegotiateAuthenticationStatusCode.ContinueNeeded);

            if (statusCode == NegotiateAuthenticationStatusCode.Completed)
            {
                // If authentication succeeded ask Windows about the identity and send it back as custom header.
                IIdentity identity = authContext.RemoteIdentity;

                authHeader = $"{UserHeaderName}: {identity.Name}\r\n";
                if (closeConnection)
                {
                    authHeader += "Connection: close\r\n";
                }

                await connection.SendResponseAsync(HttpStatusCode.OK, authHeader, "foo");

                authContext.Dispose();
            }
            else
            {
                await connection.SendResponseAsync(HttpStatusCode.Forbidden, "Connection: close\r\n", "boo");
            }
        }