private static async Task <HttpResponseMessage> SendWithNtAuthAsync(HttpRequestMessage request, Uri authUri, ICredentials credentials, bool isProxyAuth, HttpConnection connection, CancellationToken cancellationToken) { HttpResponseMessage response = await InnerSendAsync(request, isProxyAuth, connection, cancellationToken).ConfigureAwait(false); if (!isProxyAuth && connection.Kind == HttpConnectionKind.Proxy && !ProxySupportsConnectionAuth(response)) { // Proxy didn't indicate that it supports connection-based auth, so we can't proceed. if (NetEventSource.IsEnabled) { NetEventSource.Error(connection, $"Proxy doesn't support connection-based auth, uri={authUri}"); } return(response); } if (TryGetAuthenticationChallenge(response, isProxyAuth, authUri, credentials, out AuthenticationChallenge challenge)) { if (challenge.AuthenticationType == AuthenticationType.Negotiate || challenge.AuthenticationType == AuthenticationType.Ntlm) { string challengeData = challenge.ChallengeData; string spn = "HTTP/" + authUri.IdnHost; ChannelBinding channelBinding = connection.TransportContext?.GetChannelBinding(ChannelBindingKind.Endpoint); NTAuthentication authContext = new NTAuthentication(isServer: false, challenge.SchemeName, challenge.Credential, spn, ContextFlagsPal.Connection, channelBinding); try { while (true) { string challengeResponse = authContext.GetOutgoingBlob(challengeData); if (challengeResponse == null) { // Response indicated denial even after login, so stop processing and return current response. break; } await connection.DrainResponseAsync(response).ConfigureAwait(false); SetRequestAuthenticationHeaderValue(request, new AuthenticationHeaderValue(challenge.SchemeName, challengeResponse), isProxyAuth); response = await InnerSendAsync(request, isProxyAuth, connection, cancellationToken).ConfigureAwait(false); if (authContext.IsCompleted || !TryGetRepeatedChallenge(response, challenge.SchemeName, isProxyAuth, out challengeData)) { break; } } } finally { authContext.CloseContext(); } } } return(response); }
private static async Task <HttpResponseMessage> SendWithNtAuthAsync(HttpRequestMessage request, Uri authUri, ICredentials credentials, bool isProxyAuth, HttpConnection connection, CancellationToken cancellationToken) { HttpResponseMessage response = await InnerSendAsync(request, isProxyAuth, connection, cancellationToken).ConfigureAwait(false); if (TryGetAuthenticationChallenge(response, isProxyAuth, authUri, credentials, out AuthenticationChallenge challenge) && (!isProxyAuth || CheckIfProxySupportsConnectionAuth(response))) { if (challenge.AuthenticationType == AuthenticationType.Negotiate || challenge.AuthenticationType == AuthenticationType.Ntlm) { string challengeData = challenge.ChallengeData; string spn = "HTTP/" + authUri.IdnHost; ChannelBinding channelBinding = connection.TransportContext?.GetChannelBinding(ChannelBindingKind.Endpoint); NTAuthentication authContext = new NTAuthentication(false, challenge.SchemeName, challenge.Credential, spn, ContextFlagsPal.Connection, channelBinding); try { while (true) { string challengeResponse = authContext.GetOutgoingBlob(challengeData); if (authContext.IsCompleted) { break; } await connection.DrainResponseAsync(response).ConfigureAwait(false); SetRequestAuthenticationHeaderValue(request, new AuthenticationHeaderValue(challenge.SchemeName, challengeResponse), isProxyAuth); response = await InnerSendAsync(request, isProxyAuth, connection, cancellationToken).ConfigureAwait(false); if (!TryGetRepeatedChallenge(response, challenge.SchemeName, isProxyAuth, out challengeData)) { break; } } } finally { authContext.CloseContext(); } } } return(response); }
private static async Task <HttpResponseMessage> SendWithNtAuthAsync(HttpRequestMessage request, Uri authUri, ICredentials credentials, bool isProxyAuth, HttpConnection connection, HttpConnectionPool connectionPool, CancellationToken cancellationToken) { HttpResponseMessage response = await InnerSendAsync(request, isProxyAuth, connectionPool, connection, cancellationToken).ConfigureAwait(false); if (!isProxyAuth && connection.Kind == HttpConnectionKind.Proxy && !ProxySupportsConnectionAuth(response)) { // Proxy didn't indicate that it supports connection-based auth, so we can't proceed. if (NetEventSource.IsEnabled) { NetEventSource.Error(connection, $"Proxy doesn't support connection-based auth, uri={authUri}"); } return(response); } if (TryGetAuthenticationChallenge(response, isProxyAuth, authUri, credentials, out AuthenticationChallenge challenge)) { if (challenge.AuthenticationType == AuthenticationType.Negotiate || challenge.AuthenticationType == AuthenticationType.Ntlm) { bool isNewConnection = false; bool needDrain = true; try { if (response.Headers.ConnectionClose.GetValueOrDefault()) { // Server is closing the connection and asking us to authenticate on a new connection. (connection, response) = await connectionPool.CreateHttp11ConnectionAsync(request, cancellationToken).ConfigureAwait(false); if (response != null) { return(response); } connectionPool.IncrementConnectionCount(); connection.Acquire(); isNewConnection = true; needDrain = false; } if (NetEventSource.IsEnabled) { NetEventSource.Info(connection, $"Authentication: {challenge.AuthenticationType}, Uri: {authUri.AbsoluteUri.ToString()}"); } // Calculate SPN (Service Principal Name) using the host name of the request. // Use the request's 'Host' header if available. Otherwise, use the request uri. // Ignore the 'Host' header if this is proxy authentication since we need to use // the host name of the proxy itself for SPN calculation. string hostName; if (!isProxyAuth && request.HasHeaders && request.Headers.Host != null) { // Use the host name without any normalization. hostName = request.Headers.Host; if (NetEventSource.IsEnabled) { NetEventSource.Info(connection, $"Authentication: {challenge.AuthenticationType}, Host: {hostName}"); } } else { // Need to use FQDN normalized host so that CNAME's are traversed. // Use DNS to do the forward lookup to an A (host) record. // But skip DNS lookup on IP literals. Otherwise, we would end up // doing an unintended reverse DNS lookup. UriHostNameType hnt = authUri.HostNameType; if (hnt == UriHostNameType.IPv6 || hnt == UriHostNameType.IPv4) { hostName = authUri.IdnHost; } else { IPHostEntry result = await Dns.GetHostEntryAsync(authUri.IdnHost).ConfigureAwait(false); hostName = result.HostName; } } string spn = "HTTP/" + hostName; if (NetEventSource.IsEnabled) { NetEventSource.Info(connection, $"Authentication: {challenge.AuthenticationType}, SPN: {spn}"); } ChannelBinding channelBinding = connection.TransportContext?.GetChannelBinding(ChannelBindingKind.Endpoint); NTAuthentication authContext = new NTAuthentication(isServer: false, challenge.SchemeName, challenge.Credential, spn, ContextFlagsPal.Connection, channelBinding); string challengeData = challenge.ChallengeData; try { while (true) { string challengeResponse = authContext.GetOutgoingBlob(challengeData); if (challengeResponse == null) { // Response indicated denial even after login, so stop processing and return current response. break; } if (needDrain) { await connection.DrainResponseAsync(response).ConfigureAwait(false); } SetRequestAuthenticationHeaderValue(request, new AuthenticationHeaderValue(challenge.SchemeName, challengeResponse), isProxyAuth); response = await InnerSendAsync(request, isProxyAuth, connectionPool, connection, cancellationToken).ConfigureAwait(false); if (authContext.IsCompleted || !TryGetRepeatedChallenge(response, challenge.SchemeName, isProxyAuth, out challengeData)) { break; } needDrain = true; } } finally { authContext.CloseContext(); } } finally { if (isNewConnection) { connection.Release(); } } } } return(response); }