【问题标题】:WCF Access is Denied when opening named pipe channel from IIS application从 IIS 应用程序打开命名管道通道时,WCF 访问被拒绝
【发布时间】:2018-05-06 20:12:58
【问题描述】:

我们有一些旧的 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 帖子有一些相似之处:How to call net.pipe (named pipe) WCF services while impersonating in a Windows Service,只是没有发生明确的模拟,所以我不确定修复是否适用。

另外需要注意的是,当我尝试调用System.Diagnostics.Process.GetCurrentProcess() 时,会抛出相同的错误。尝试获取当前执行进程的句柄时也会出现该错误。

【问题讨论】:

  • 命名管道 WCF 服务是否托管在 IIS 中?您是否有能力以其他方式托管它(例如自托管或 Windows 服务)?
  • @lesscode 它托管在带有 WAS 激活的 IIS 中。但是,正如我所提到的,错误并非源自服务端;打开频道时它在客户端失败。 (客户端也在 IIS 中)。如果我从控制台应用程序运行客户端,它工作正常。
  • 我的猜测是您的控制台应用程序可以运行提升,而您的 Sharepoint Web 应用程序没有。我认为(我的 WCF 有点生疏)WCF 命名管道只能在与交互式用户相同的登录会话中访问,除非进程可以创建全局(而不是本地)内核对象用于解析管道名称。我会看看我是否可以从前世挖掘出一些更详细的笔记......
  • 奇怪的是,应用程序池帐户是管理员帐户,而我用来连接的 Windows 帐户也是管理员帐户。当我通过代码进行调试时,HttpContext 用户被报告为管理员帐户。在之前的 3.5 版本(.NET 2.0 运行时)中,这可以正常工作(可能是一些配置差异,我还没有发现?)
  • 啊,我也发现这个响了几声:stackoverflow.com/questions/3366976/…

标签: wcf named-pipes asp.net-4.0 code-access-security


【解决方案1】:

我得出的结论是,此问题与 .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 有一些依赖,所以我切换起来并不是那么简单。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2020-10-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-05-06
    • 1970-01-01
    • 1970-01-01
    • 2014-12-22
    相关资源
    最近更新 更多