代码之家  ›  专栏  ›  技术社区  ›  Charles Chen

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

  •  2
  • Charles Chen  · 技术社区  · 6 年前

    我们有一些遗留的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);
    

    web上的各种主题建议删除元素或设置 impersonate="false" :

    <system.web>
      <identity impersonate="true"/>
    </system.web>
    

    事实上,这可以解决我的问题。但是,我不确定这可能会对应用程序(SharePoint 2016)产生什么副作用,因此我不愿意简单地删除此属性。

    这个 SecurityCritical 属性给了我一些提示,可能这与CAS模型之间的更改有关。NET 2.0和。净额4.0。代码已安装到GAC中,因此它应该已经在完全信任的情况下运行,但我还是尝试了一下。

    我还尝试添加 [SecuritySafeCritical] 调用 IChannel.Open() 无济于事。

    我还尝试添加 [assembly: SecurityRules(SecurityRuleSet.Level1)] 在总成上,因为这应锁定在。NET Framework 2.0安全规则。

    我正在寻找任何其他见解和其他方法来尝试解决此问题。

    这与另一篇文章有一些相似之处: How to call net.pipe (named pipe) WCF services while impersonating in a Windows Service 除了没有发生显式模拟,所以我不确定修复是否适用。

    另一个注意事项是,当我试图打电话时 System.Diagnostics.Process.GetCurrentProcess() ,将引发相同的错误。尝试获取当前正在执行的进程的句柄时,也会产生此错误。

    1 回复  |  直到 6 年前
        1
  •  0
  •   Charles Chen    6 年前

    我的结论是,这些问题与 System.ServiceModel 在里面净额4.0。

    起初,我认为这可能与Server 2016 UAC或有关。NET 4.0/IIS 10 web应用程序运行时设置(例如,NET 2.0与NET 4.0 CAS机型)。我在中创建了一个简单的web应用程序。NET 3.5并尝试调用 Process.GetCurrentProcess().Handle 。我在新服务器上运行了此操作,但失败了,出现了相同的“拒绝访问”错误。

    我把它放到旧服务器(Windows server 2008 R2,.NET 3.5)中,并在那里运行,希望它能正常工作,瞧,它也失败了。所以我浏览了 系统服务模型 在3.5中发现 AppContainerInfo 因此,3.5代码可能根本不会进行相同的Win32 API级别调用。

    我的结论是,我们以前没有遇到过这个错误,因为旧的3.0库不需要从调用API advapi32.dll 或者使用其他机制来创建管道名称。

    实际上,以下是 PipeConnectionInitiator.GetPipeName 在3.0中:

    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>
    

    并简单地强制运行时绑定到旧版本。

    不幸的是,应用程序对 系统服务模型 4.0所以切换对我来说并不那么简单。