Отказано в доступе к WCF при открытии канала именованного канала из приложения IIS

У нас есть устаревший код веб-приложения, который мы обновляем и переносим в среду выполнения .NET 4.0.

Код находится в библиотеке классов и подключается к конечной точке именованного канала с помощью WCF.

Когда я инициирую соединение из консольного приложения, все работает нормально.

Когда я инициирую соединение из веб-приложения, я получаю исключение:

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 подсказал мне, что, возможно, это связано с изменением модели CAS между .NET 2.0 и .NET 4.0. Код установлен в GAC, поэтому он уже должен работать с полным доверием, но я все равно попробовал.

Я также пытался добавить [SecuritySafeCritical] к методу и классу, который вызывает IChannel.Open(), но безрезультатно.

Я также попытался добавить [assembly: SecurityRules(SecurityRuleSet.Level1)] в сборку, так как это должно соответствовать правилам безопасности .NET Framework 2.0.

Я ищу любую дополнительную информацию и другие методы, чтобы попытаться решить эту проблему.

Есть некоторое сходство с этим другим сообщением о стеке: -a-windows">Как вызывать службы WCF net.pipe (named pipe) во время олицетворения в службе Windows, за исключением того, что явное олицетворение не происходит, поэтому я не уверен, что исправление применимо.

Дополнительным примечанием является то, что когда я пытаюсь вызвать System.Diagnostics.Process.GetCurrentProcess(), возникает та же ошибка. Ошибка также возникает при попытке получить дескриптор текущего исполняемого процесса.


person Charles Chen    schedule 06.05.2018    source источник
comment
Размещена ли служба WCF именованного канала в IIS? Есть ли у вас возможность разместить его другим способом (например, самостоятельно или через службу Windows)?   -  person lesscode    schedule 06.05.2018
comment
@lesscode размещается в IIS с активацией WAS. Однако, как я уже упоминал, ошибка возникает не со стороны службы; он не работает на стороне клиента при открытии канала. (Клиент тоже в IIS). Если я запускаю клиент из консольного приложения, он работает нормально.   -  person Charles Chen    schedule 06.05.2018
comment
Я предполагаю, что ваше консольное приложение будет работать с повышенными правами, а ваше веб-приложение Sharepoint — нет. Я думаю (мой WCF немного заржавел), что именованные каналы WCF могут быть доступны только в том же сеансе входа в систему, что и интерактивный пользователь, если только процесс не может создать глобальный (а не локальный) объект ядра. для разрешения имен каналов. Я посмотрю, смогу ли я откопать более подробные заметки об этом из предыдущих жизней...   -  person lesscode    schedule 07.05.2018
comment
Странно то, что учетная запись пула приложений является учетной записью администратора, а учетная запись Windows, которую я использую для подключения, также является учетной записью администратора. Когда я отлаживаю код, пользователь HttpContext сообщается как учетная запись администратора. В предыдущей версии 3.5 (среда выполнения .NET 2.0) это работало нормально (возможно, какая-то разница в конфигурации, которую я еще не нашел?)   -  person Charles Chen    schedule 07.05.2018
comment
Ах, я также нашел это, которое звонило в несколько колоколов: заголовок stackoverflow.com/questions/3366976/   -  person lesscode    schedule 07.05.2018
comment
Я проверил эту ссылку, и точка отказа кажется другой. Я даже использовал Wayback Machine для просмотра страниц, на которые ссылается Крис Диксон. Сбой в сценарии, задокументированном Крисом, кажется, связан со списками управления доступом в именованном канале. Однако я считаю, что проблема, с которой я сталкиваюсь, возникает до того, как она получит доступ к именованному каналу; на самом деле это происходит при разрешении адреса именованного канала; он не может сделать необходимый вызов в advapi32.dll. Я попытался использовать OpenProcessToken, чтобы заставить токен процесса олицетворяться, и получил ту же ошибку.   -  person Charles Chen    schedule 07.05.2018
comment
Может быть, это общий объект ядра, к которому вы не можете получить доступ (по той же причине)? Хотя это кажется маловероятным, поскольку стек жалуется на токен процесса. О вне идей. Не похоже, чтобы Крис был здесь более активным — держу пари, у него было бы больше понимания.   -  person lesscode    schedule 07.05.2018
comment
Не уверен, что вы нашли это в своем поиске: social.msdn.microsoft.com/Forums/vstudio/en-US/   -  person lesscode    schedule 07.05.2018


Ответы (1)


Я пришел к выводу, что эти проблемы связаны с внутренним устройством System.ServiceModel в .NET 4.0.

Первоначально я думал, что это может быть связано с настройками времени выполнения веб-приложений Server 2016 UAC или .NET 4.0/IIS 10 (например, модели .NET 2.0 и .NET 4.0 CAS). Я создал простое веб-приложение в .NET 3.5 и попытался вызвать Process.GetCurrentProcess().Handle. Я запустил это на новом сервере, и он потерпел неудачу с той же ошибкой «Отказано в доступе».

Я взял это на старый сервер (Windows Server 2008 R2, .NET 3.5) и запустил его там, ожидая, что это сработает, и о чудо, это также не работает. Итак, я просмотрел источник System.ServiceModel в 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>

И просто заставьте среду выполнения привязываться к старым версиям.

К сожалению, приложение имеет некоторые зависимости от System.ServiceModel 4.0 поэтому мне не так просто перейти.

person Charles Chen    schedule 07.05.2018