我们有一些遗留的Web应用程序代码,我们正在更新这些代码并将其移植到.NET 4.0运行时中。
该代码位于类库中,并使用 WCF 连接到命名管道终结点。
当我从控制台应用程序启动连接时,一切正常。
当我从 Web 应用程序启动连接时,我收到异常:
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)
该错误源于托管代码和非托管代码之间的边界,其中调用了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);
网络上的各种主题建议删除元素或设置impersonate="false"
:
<system.web>
<identity impersonate="true"/>
</system.web>
事实上,这可以解决我的问题。 但是,我不确定这可能会对应用程序(SharePoint 2016(产生什么副作用,因此我不愿意简单地删除此属性。
SecurityCritical
属性给了我一些提示,因为这可能与 .NET 2.0 和 .NET 4.0 之间 CAS 模型的变化有关。 该代码已安装到 GAC 中,因此它应该已经在完全信任下运行,但我还是试了一下。
我还尝试将[SecuritySafeCritical]
添加到调用IChannel.Open()
的方法和类中,但无济于事。
我还尝试在程序集上添加[assembly: SecurityRules(SecurityRuleSet.Level1)]
,因为这应该锁定到 .NET Framework 2.0 安全规则中。
我正在寻找任何其他见解和其他方法来尝试解决此问题。
与另一篇 Stack 帖子有一些相似之处:如何在 Windows 服务中模拟时调用 net.pipe(命名管道(WCF 服务,除了没有发生显式模拟,所以我不确定修复是否适用。
另外需要注意的是,当我尝试调用System.Diagnostics.Process.GetCurrentProcess()
时,会抛出相同的错误。 尝试获取当前执行进程的句柄时也会产生此错误。
我得出的结论是,这个问题与 .NET 4.0 中System.ServiceModel
的内部结构有关。
最初,我认为这可能与Server 2016 UAC或.NET 4.0/IIS 10 Web应用程序运行时设置(例如.NET 2.0与.NET 4.0 CAS模型(有关。 我在 .NET 3.5 中创建一个简单的 Web 应用程序并尝试调用Process.GetCurrentProcess().Handle
。 我在新服务器中运行了它,它失败了,并出现相同的"访问被拒绝"错误。
我把它放到旧服务器(Windows Server 2008 R2,.NET 3.5(中并在那里运行它,期望它能工作,瞧,它也失败了。 所以我在 3.5 中浏览了System.ServiceModel
源代码,发现没有AppContainerInfo
,因此 3.5 代码很可能根本不进行相同的 Win32 API 级别调用。
我的结论是,我们之前没有遇到此错误,因为旧的 3.0 库不需要从advapi32.dll
调用 API,或者有其他机制来创建管道名称。
事实上,这是 3.0 中PipeConnectionInitiator.GetPipeName
实现的前几行":
internal static string GetPipeName(Uri uri)
{
string[] strArray = new string[3]
{
"+",
uri.Host,
"*"
};
bool[] flagArray = new bool[2]{ true, false };
for (int index1 = 0; index1 < strArray.Length; ++index1)
{
for (int index2 = 0; index2 < flagArray.Length; ++index2)
{
这是 4.0 中的前几行:
internal static string GetPipeName(Uri uri, IPipeTransportFactorySettings transportFactorySettings)
{
AppContainerInfo appContainerInfo = PipeConnectionInitiator.GetAppContainerInfo(transportFactorySettings);
string[] strArray = new string[3]
{
"+",
uri.Host,
"*"
};
bool[] flagArray = new bool[2]{ true, false };
string str1 = string.Empty;
string str2 = (string) null;
for (int index1 = 0; index1 < strArray.Length; ++index1)
{
for (int index2 = 0; index2 < flagArray.Length; ++index2)
{
if (appContainerInfo == null || !flagArray[index2])
所以4.0实现需要访问才能执行OpenProcessToken
。
如果代码已充分隔离,则一种选择是使用程序集绑定重定向:
<runtime>
<assemblyBinding>
<dependentAssembly>
<assemblyIdentity name="System.ServiceProcess" publicKeyToken="b77a5c561934e089" culture="neutral" />
<bindingRedirect oldVersion="4.0.0.0" newVersion="3.0.0.0" />
</dependentAssembly>
</assemblyBinding>
</runtime>
只需强制运行时绑定到旧版本即可。
不幸的是,该应用程序对System.ServiceModel
4.0有一些依赖性,因此切换对我来说并不那么简单。