From: Alexander Nikolaev <55398552+alnikola@users.noreply.github.com> Date: Mon, 2 Dec 2019 14:15:43 +0000 (+0100) Subject: Current user identity in added to HttpConnectionKey (#303) X-Git-Tag: submit/tizen/20210909.063632~10851 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=3cec4f50af9fc68bcde80952427092f7a8873864;p=platform%2Fupstream%2Fdotnet%2Fruntime.git Current user identity in added to HttpConnectionKey (#303) On retrieving a connection from a pool, HttpConnectionPoolManager adds the current user identity to the HttpConnectionKey for direct and proxy connections when the default credentials is used on Windows platform. Since on Unix there is not the concept of a user identity on the thread, the identity component in the key is always set to string.Empty. Fixes dotnet/corefx#39621 --- diff --git a/src/libraries/System.Net.Http/src/System.Net.Http.csproj b/src/libraries/System.Net.Http/src/System.Net.Http.csproj index 111e336..63eec675 100644 --- a/src/libraries/System.Net.Http/src/System.Net.Http.csproj +++ b/src/libraries/System.Net.Http/src/System.Net.Http.csproj @@ -243,6 +243,7 @@ + Common\System\Net\ContextFlagsAdapterPal.Unix.cs @@ -330,6 +331,7 @@ + Common\Interop\Windows\SChannel\Interop.SecPkgContext_ApplicationProtocol.cs @@ -631,11 +633,13 @@ + + diff --git a/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/CurrentUserIdentityProvider.Unix.cs b/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/CurrentUserIdentityProvider.Unix.cs new file mode 100644 index 0000000..0950e71 --- /dev/null +++ b/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/CurrentUserIdentityProvider.Unix.cs @@ -0,0 +1,14 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace System.Net.Http +{ + internal static class CurrentUserIdentityProvider + { + public static string GetIdentity() + { + return string.Empty; + } + } +} diff --git a/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/CurrentUserIdentityProvider.Windows.cs b/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/CurrentUserIdentityProvider.Windows.cs new file mode 100644 index 0000000..c79ae98 --- /dev/null +++ b/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/CurrentUserIdentityProvider.Windows.cs @@ -0,0 +1,17 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Security.Principal; + +namespace System.Net.Http +{ + internal static class CurrentUserIdentityProvider + { + public static string GetIdentity() + { + using WindowsIdentity identity = WindowsIdentity.GetCurrent(); + return identity.Name; + } + } +} diff --git a/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/HttpConnectionPoolManager.cs b/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/HttpConnectionPoolManager.cs index 03db401..1fc178d 100644 --- a/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/HttpConnectionPoolManager.cs +++ b/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/HttpConnectionPoolManager.cs @@ -162,14 +162,14 @@ namespace System.Net.Http return hostHeader; } - private static HttpConnectionKey GetConnectionKey(HttpRequestMessage request, Uri proxyUri, bool isProxyConnect) + private HttpConnectionKey GetConnectionKey(HttpRequestMessage request, Uri proxyUri, bool isProxyConnect) { Uri uri = request.RequestUri; if (isProxyConnect) { Debug.Assert(uri == proxyUri); - return new HttpConnectionKey(HttpConnectionKind.ProxyConnect, uri.IdnHost, uri.Port, null, proxyUri); + return new HttpConnectionKey(HttpConnectionKind.ProxyConnect, uri.IdnHost, uri.Port, null, proxyUri, GetIdentityIfDefaultCredentialsUsed(_settings._defaultCredentialsUsedForProxy)); } string sslHostName = null; @@ -187,6 +187,8 @@ namespace System.Net.Http } } + string identity = GetIdentityIfDefaultCredentialsUsed(proxyUri != null ? _settings._defaultCredentialsUsedForProxy : _settings._defaultCredentialsUsedForServer); + if (proxyUri != null) { Debug.Assert(HttpUtilities.IsSupportedNonSecureScheme(proxyUri.Scheme)); @@ -195,29 +197,29 @@ namespace System.Net.Http if (HttpUtilities.IsNonSecureWebSocketScheme(uri.Scheme)) { // Non-secure websocket connection through proxy to the destination. - return new HttpConnectionKey(HttpConnectionKind.ProxyTunnel, uri.IdnHost, uri.Port, null, proxyUri); + return new HttpConnectionKey(HttpConnectionKind.ProxyTunnel, uri.IdnHost, uri.Port, null, proxyUri, identity); } else { // Standard HTTP proxy usage for non-secure requests // The destination host and port are ignored here, since these connections // will be shared across any requests that use the proxy. - return new HttpConnectionKey(HttpConnectionKind.Proxy, null, 0, null, proxyUri); + return new HttpConnectionKey(HttpConnectionKind.Proxy, null, 0, null, proxyUri, identity); } } else { // Tunnel SSL connection through proxy to the destination. - return new HttpConnectionKey(HttpConnectionKind.SslProxyTunnel, uri.IdnHost, uri.Port, sslHostName, proxyUri); + return new HttpConnectionKey(HttpConnectionKind.SslProxyTunnel, uri.IdnHost, uri.Port, sslHostName, proxyUri, identity); } } else if (sslHostName != null) { - return new HttpConnectionKey(HttpConnectionKind.Https, uri.IdnHost, uri.Port, sslHostName, null); + return new HttpConnectionKey(HttpConnectionKind.Https, uri.IdnHost, uri.Port, sslHostName, null, identity); } else { - return new HttpConnectionKey(HttpConnectionKind.Http, uri.IdnHost, uri.Port, null, null); + return new HttpConnectionKey(HttpConnectionKind.Http, uri.IdnHost, uri.Port, null, null, identity); } } @@ -408,6 +410,11 @@ namespace System.Net.Http if (NetEventSource.IsEnabled) NetEventSource.Exit(this); } + private static string GetIdentityIfDefaultCredentialsUsed(bool defaultCredentialsUsed) + { + return defaultCredentialsUsed ? CurrentUserIdentityProvider.GetIdentity() : string.Empty; + } + internal readonly struct HttpConnectionKey : IEquatable { public readonly HttpConnectionKind Kind; @@ -415,21 +422,23 @@ namespace System.Net.Http public readonly int Port; public readonly string SslHostName; // null if not SSL public readonly Uri ProxyUri; + public readonly string Identity; - public HttpConnectionKey(HttpConnectionKind kind, string host, int port, string sslHostName, Uri proxyUri) + public HttpConnectionKey(HttpConnectionKind kind, string host, int port, string sslHostName, Uri proxyUri, string identity) { Kind = kind; Host = host; Port = port; SslHostName = sslHostName; ProxyUri = proxyUri; + Identity = identity; } // In the common case, SslHostName (when present) is equal to Host. If so, don't include in hash. public override int GetHashCode() => (SslHostName == Host ? - HashCode.Combine(Kind, Host, Port, ProxyUri) : - HashCode.Combine(Kind, Host, Port, SslHostName, ProxyUri)); + HashCode.Combine(Kind, Host, Port, ProxyUri, Identity) : + HashCode.Combine(Kind, Host, Port, SslHostName, ProxyUri, Identity)); public override bool Equals(object obj) => obj != null && @@ -441,7 +450,8 @@ namespace System.Net.Http Host == other.Host && Port == other.Port && ProxyUri == other.ProxyUri && - SslHostName == other.SslHostName; + SslHostName == other.SslHostName && + Identity == other.Identity; } } } diff --git a/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/HttpConnectionSettings.cs b/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/HttpConnectionSettings.cs index f678873..6508349 100644 --- a/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/HttpConnectionSettings.cs +++ b/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/HttpConnectionSettings.cs @@ -23,6 +23,8 @@ namespace System.Net.Http internal bool _useProxy = HttpHandlerDefaults.DefaultUseProxy; internal IWebProxy _proxy; internal ICredentials _defaultProxyCredentials; + internal bool _defaultCredentialsUsedForProxy; + internal bool _defaultCredentialsUsedForServer; internal bool _preAuthenticate = HttpHandlerDefaults.DefaultPreAuthenticate; internal ICredentials _credentials; @@ -53,6 +55,8 @@ namespace System.Net.Http bool allowHttp2 = AllowHttp2; _maxHttpVersion = allowHttp2 ? HttpVersion.Version20 : HttpVersion.Version11; _allowUnencryptedHttp2 = allowHttp2 && AllowUnencryptedHttp2; + _defaultCredentialsUsedForProxy = _proxy != null && (_proxy.Credentials == CredentialCache.DefaultCredentials || _defaultProxyCredentials == CredentialCache.DefaultCredentials); + _defaultCredentialsUsedForServer = _credentials == CredentialCache.DefaultCredentials; } /// Creates a copy of the settings but with some values normalized to suit the implementation. @@ -72,6 +76,8 @@ namespace System.Net.Http _connectTimeout = _connectTimeout, _credentials = _credentials, _defaultProxyCredentials = _defaultProxyCredentials, + _defaultCredentialsUsedForProxy = _defaultCredentialsUsedForProxy, + _defaultCredentialsUsedForServer = _defaultCredentialsUsedForServer, _expect100ContinueTimeout = _expect100ContinueTimeout, _maxAutomaticRedirections = _maxAutomaticRedirections, _maxConnectionsPerServer = _maxConnectionsPerServer, diff --git a/src/libraries/System.Net.Http/tests/FunctionalTests/HttpConnectionKeyTest.cs b/src/libraries/System.Net.Http/tests/FunctionalTests/HttpConnectionKeyTest.cs new file mode 100644 index 0000000..6e71bd1 --- /dev/null +++ b/src/libraries/System.Net.Http/tests/FunctionalTests/HttpConnectionKeyTest.cs @@ -0,0 +1,38 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using System.Reflection; +using Xunit; + +namespace System.Net.Http.Functional.Tests +{ + public class HttpConnectionKeyTest + { + public static IEnumerable KeyComponents() + { + yield return new object[] { "Https", "localhost", 80, "localhost-ssl", new Uri("http://localhost"), "domain1/userA", false}; + yield return new object[] { "Http", "localhost1", 80, "localhost-ssl", new Uri("http://localhost"), "domain1/userA", false }; + yield return new object[] { "Http", "localhost", 81, "localhost-ssl", new Uri("http://localhost"), "domain1/userA", false }; + yield return new object[] { "Http", "localhost", 80, "localhost-ssl1", new Uri("http://localhost"), "domain1/userA", false }; + yield return new object[] { "Http", "localhost", 80, "localhost-ssl", new Uri("http://localhost1"), "domain1/userA", false }; + yield return new object[] { "Http", "localhost", 80, "localhost-ssl", new Uri("http://localhost"), "domain1/userB", false }; + yield return new object[] { "Http", "localhost", 80, "localhost-ssl", new Uri("http://localhost"), "domain1/userA", true }; + } + + [Theory, MemberData(nameof(KeyComponents))] + public void Equals_DifferentParameters_ReturnsTrueIfAllEqual(string kindString, string host, int port, string sslHostName, Uri proxyUri, string identity, bool expected) + { + Assembly assembly = typeof(HttpClientHandler).Assembly; + Type connectionKindType = assembly.GetTypes().Where(t => t.Name == "HttpConnectionKind").First(); + Type poolManagerType = assembly.GetTypes().Where(t => t.Name == "HttpConnectionPoolManager").First(); + Type keyType = poolManagerType.GetNestedType("HttpConnectionKey", BindingFlags.NonPublic); + dynamic referenceKey = Activator.CreateInstance(keyType, Enum.Parse(connectionKindType, "Http"), "localhost", 80, "localhost-ssl", new Uri("http://localhost"), "domain1/userA"); + dynamic actualKey = Activator.CreateInstance(keyType, Enum.Parse(connectionKindType, kindString), host, port, sslHostName, proxyUri, identity); + Assert.Equal(expected, referenceKey.Equals(actualKey)); + } + } +} diff --git a/src/libraries/System.Net.Http/tests/FunctionalTests/System.Net.Http.Functional.Tests.csproj b/src/libraries/System.Net.Http/tests/FunctionalTests/System.Net.Http.Functional.Tests.csproj index c10bedc..b5a374c 100644 --- a/src/libraries/System.Net.Http/tests/FunctionalTests/System.Net.Http.Functional.Tests.csproj +++ b/src/libraries/System.Net.Http/tests/FunctionalTests/System.Net.Http.Functional.Tests.csproj @@ -115,6 +115,7 @@ +