We have some legacy web application code which we are updating and porting into a .NET 4.0 runtime.
The code is in a class library and connects to a named pipe endpoint using WCF.
When I initiate the connection from a console application, everything works fine.
When I initiate the connection from a web application, I receive an exception:
Access is denied
Server stack trace:
at System.ServiceModel.Channels.AppContainerInfo.GetCurrentProcessToken()
at System.ServiceModel.Channels.AppContainerInfo.RunningInAppContainer()
at System.ServiceModel.Channels.AppContainerInfo.get_IsRunningInAppContainer()
at System.ServiceModel.Channels.PipeSharedMemory.BuildPipeName(String pipeGuid)
at System.ServiceModel.Channels.PipeSharedMemory.get_PipeName()
at System.ServiceModel.Channels.PipeConnectionInitiator.GetPipeName(Uri uri, IPipeTransportFact… Object[] , Object[] )
at System.ServiceModel.Dispatcher.SyncMethodInvoker.Invoke(Object instance, Object[] inputs, Object[]& outputs)
at System.ServiceModel.Dispatcher.DispatchOperationRuntime.InvokeBegin(MessageRpc& rpc)
at System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage5(MessageRpc& rpc)
at System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage11(MessageRpc& rpc)
at System.ServiceModel.Dispatcher.MessageRpc.Process(Boolean isOperationContextSet)
The error originates at a boundary between the managed code and unmanaged code where there is a call into advapi32.dll:
[SecurityCritical]
private static SafeCloseHandle GetCurrentProcessToken()
{
SafeCloseHandle TokenHandle = (SafeCloseHandle) null;
if (!UnsafeNativeMethods.OpenProcessToken(UnsafeNativeMethods.GetCurrentProcess(), TokenAccessLevels.Query, out TokenHandle))
throw System.ServiceModel.FxTrace.Exception.AsError((Exception) new Win32Exception(Marshal.GetLastWin32Error()));
return TokenHandle;
}
[DllImport("advapi32.dll", SetLastError = true)]
internal static extern bool OpenProcessToken(IntPtr ProcessHandle, TokenAccessLevels DesiredAccess, out SafeCloseHandle TokenHandle);
Various topics on the web suggest removing the element or setting impersonate="false":
<system.web>
<identity impersonate="true"/>
</system.web>
And indeed, this works to fix my issue. However, I'm not certain what side effects this may have on the application (SharePoint 2016) so I'm reluctant to simply remove this attribute.
The SecurityCritical attribute gave me some hints in that perhaps this is related to the change in the CAS model between .NET 2.0 and .NET 4.0. The code is installed into the GAC so it should be running under full trust already but I gave it a shot anyways.
I have also tried adding [SecuritySafeCritical] to the method and the class which invokes the IChannel.Open() to no avail.
I also tried adding [assembly: SecurityRules(SecurityRuleSet.Level1)] on the assembly as this should lock into the .NET Framework 2.0 security rules.
I'm looking for any additional insight and other methods to try to resolve this issue.
There is some similarity to this other Stack post: How to call net.pipe (named pipe) WCF services while impersonating in a Windows Service except that there is no explicit impersonation occurring so I'm not certain that the fix would apply.
An additional note is that when I try to call System.Diagnostics.Process.GetCurrentProcess(), the same error is thrown. The error also originates when trying to get a handle on the current executing process.
I've concluded that this issues is related to the internals of
System.ServiceModelin .NET 4.0.Initially, I thought that this may be related to Server 2016 UAC or .NET 4.0/IIS 10 web application runtime settings (e.g. .NET 2.0 vs .NET 4.0 CAS models). I created a simple web application in .NET 3.5 and tried to call
Process.GetCurrentProcess().Handle. I ran this in the new server and it failed with the same "Access is denied" error.I took this into the old server (Windows Server 2008 R2, .NET 3.5) and ran it there expecting this to work and lo-and-behold, it also fails. So I browsed the source for
System.ServiceModelin 3.5 and found that there is noAppContainerInfoand thus it is likely that the 3.5 code does not make the same Win32 API level calls at all.My conclusion is that we did not encounter this error before because the old 3.0 libraries did not need to invoke APIs from
advapi32.dllor had some other mechanism to create the pipe name.Indeed, here is the first few lines of the implementation from
PipeConnectionInitiator.GetPipeNamein 3.0":And here is the first few lines in 4.0:
So the 4.0 implementation requires access to execute
OpenProcessToken.One option, if the code is sufficiently isolated, is to use an assembly binding redirect:
And simply force the runtime to bind to the old versions.
Unfortunately, the application has some dependencies on
System.ServiceModel4.0 so it's not so simple for me to switch.