从 IIS 应用程序打开命名管道通道时,WCF 访问被拒绝



我们有一些遗留的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.ServiceModel4.0有一些依赖性,因此切换对我来说并不那么简单。

最新更新