Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

NullReferenceException when using proxy server with NTLM authentication and BasicHttpBinding (... and potentially others) #4557

Closed
fondencn opened this issue Mar 9, 2021 · 6 comments
Assignees
Labels
bug This is a product bug.

Comments

@fondencn
Copy link

fondencn commented Mar 9, 2021

When using BasicHttpBinding (or potentially others) behind a web proxy that requires authentication, the authenticationScheme cannot be used, as when it comes to opening the http channel, a NullReferenceException is raised in System.Private.ServiceModel.dll when trying to use an uninialized class member.

Root cause: the credential manager only gets instantiated when using authentication against the service itsself but the proxy authentication, which actually can be different (and is in most cases in large enterprise scenarios) is being ignored, even if passed by the used binding.

Code to reproduce:

 private Binding CreateBinding()
 {
           BasicHttpBinding binding = new BasicHttpBinding();
            if (this.ServiceUri.OriginalString.StartsWith("https://", StringComparison.OrdinalIgnoreCase))
            {
                binding.Security.Mode = BasicHttpSecurityMode.Transport;
            }
            else
            {
                binding.Security.Mode = BasicHttpSecurityMode.None;
            }
            binding.CloseTimeout = TimeSpan.FromSeconds(2);
            binding.OpenTimeout = TimeSpan.FromSeconds(5);
            binding.ReceiveTimeout = TimeSpan.FromMinutes(2);
            binding.SendTimeout = TimeSpan.FromMinutes(2);
            binding.MaxBufferSize = Int32.MaxValue;
            binding.MaxReceivedMessageSize = Int32.MaxValue;
            binding.AllowCookies = true;


            CustomBinding customBinding = new CustomBinding(binding);
            if (this.IsProxyEnabled)
            {
                HttpTransportBindingElement httpElement = customBinding.Elements.Find<HttpTransportBindingElement>();
                httpElement.UseDefaultWebProxy = false;
                httpElement.ProxyAddress = new Uri($"http://{this.ServiceSettings.ProxyHostname}:{this.ServiceSettings.ProxyPort}");
                httpElement.ProxyAuthenticationScheme = AuthenticationSchemes.Ntlm; // produces NullReferenceException in WCF client (master) during execution
                //httpElement.AuthenticationScheme = AuthenticationSchemes.Ntlm; // nicht richtig - das hier wäre für die authentifizierung am Webservice selbst
                httpElement.BypassProxyOnLocal = false;
            }
            return customBinding;
}
@dimalyshev
Copy link
Contributor

dimalyshev commented Mar 12, 2021

Hope this patch helps (i've tested on my local machine behind corporate proxy with Basic authentication):

This lightweigt patch enables to clients to provide fully specified WebProxy as required to their environment.
How soon it could be in nugets (#4563)?

It's very critical bug and stops any WCF usage in typical Enterprise environment.

Thanx in advance.


    fix WCF client Proxy

diff --git a/System.Private.ServiceModel/src/System/ServiceModel/Channels/HttpChannelFactory.cs b/System.Private.ServiceModel/src/System/ServiceModel/Channels/HttpChannelFactory.cs
--- a/System.Private.ServiceModel/src/System/ServiceModel/Channels/HttpChannelFactory.cs
+++ b/System.Private.ServiceModel/src/System/ServiceModel/Channels/HttpChannelFactory.cs
@@ -100,7 +100,9 @@ namespace System.ServiceModel.Channels
             TransferMode = bindingElement.TransferMode;
             _keepAliveEnabled = bindingElement.KeepAliveEnabled;
 
-            if (bindingElement.ProxyAddress != null)
+            if (bindingElement.Proxy != null)
+                _proxy = bindingElement.Proxy;
+            else if (bindingElement.ProxyAddress != null)
             {
                 if (bindingElement.UseDefaultWebProxy)
                 {
diff --git a/System.Private.ServiceModel/src/System/ServiceModel/Channels/HttpTransportBindingElement.cs b/System.Private.ServiceModel/src/System/ServiceModel/Channels/HttpTransportBindingElement.cs
index 85bdb4e..7303f6a 100644
--- a/System.Private.ServiceModel/src/System/ServiceModel/Channels/HttpTransportBindingElement.cs
+++ b/System.Private.ServiceModel/src/System/ServiceModel/Channels/HttpTransportBindingElement.cs
@@ -80,6 +80,8 @@ namespace System.ServiceModel.Channels
             MessageHandlerFactory = elementToBeCloned.MessageHandlerFactory;
         }
 
+        public IWebProxy Proxy { get; set; }
+
         [DefaultValue(HttpTransportDefaults.AllowCookies)]
         public bool AllowCookies { get; set; }
 

@dimalyshev
Copy link
Contributor

httpElement.ProxyAuthenticationScheme = AuthenticationSchemes.Basic; // produce UsernameSecurityToken error

@mconnew
Copy link
Member

mconnew commented Mar 12, 2021

@dimalyshev, can you provide the call stack?

@dimalyshev
Copy link
Contributor

dimalyshev commented Mar 12, 2021

@mconnew, sorry in advance for mess with this 2 prev PR - I really don't know how to purge these artifacts from
issue history. But #4563 really fixes issue with Proxy with Basic auth and gets relief with all this nightmares about configuring binding proxy.

The exception call stack for #4562 is here:

System.InvalidOperationException: The required UserNameSecurityToken was not provided.
   at System.Runtime.AsyncResult.End[TAsyncResult](IAsyncResult result) in \wcf\System.Private.ServiceModel\src\Internals\System\Runtime\AsyncResult.cs:line 354
   at System.ServiceModel.Channels.ServiceChannel.SendAsyncResult.End(SendAsyncResult result) in \wcf\System.Private.ServiceModel\src\System\ServiceModel\Channels\ServiceChannel.cs:line 1909
   at System.ServiceModel.Channels.ServiceChannel.EndCall(String action, Object[] outs, IAsyncResult result) in \wcf\System.Private.ServiceModel\src\System\ServiceModel\Channels\ServiceChannel.cs:line 806
   at System.ServiceModel.Channels.ServiceChannelProxy.TaskCreator.<>c__DisplayClass1_0.<CreateGenericTask>b__0(IAsyncResult asyncResult) in \wcf\System.Private.ServiceModel\src\System\ServiceModel\Channels\ServiceChannelProxy.cs:line 184
--- End of stack trace from previous location where exception was thrown ---

@ButaevSergey
Copy link

Is it an option to manually pass Proxy Authentication header on the Transport level via custom implementation of the IClientMessageInspector in the meantime?

@dimalyshev
Copy link
Contributor

@ButaevSergey , the main idea of fix is to do initing _proxy used in HttpChannelFactory.GetHttpClientAsync more clear and client driven - not framework driven, because as stated in #4562 even Basic auth for proxy doesn't work

Meanwhile all previous mechanics of interception and configuring transport level are kept the same.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug This is a product bug.
Projects
None yet
Development

No branches or pull requests

5 participants