Как загрузить в домен свою сборку со статусом "полностью доверенная"?
От: Коваленко Дмитрий Россия http://www.ibprovider.com
Дата: 28.10.13 16:34
Оценка:
Привет всем.

[VS2012, .NET 4.5]

По мотивам статьи "Управление доступом для кода и ADO.NET" накатал тест, который работает как ожидалось:

Вкратце:
— Создается отдельный домен с минимальными правами.
— Внутри этого домена создается тестовый объект.
— Тестовый объект пытается открыть OLEDB-подключение (System.Data.OleDb.Connection).

  Скрытый текст
////////////////////////////////////////////////////////////////////////////////
using System;
using System.Reflection;
using System.Security;
using System.Security.Policy;
using System.Security.Permissions;
using NUnit;
using NUnit.Framework;

namespace lcpi_data_oledb_tests.OleDbPermission{
////////////////////////////////////////////////////////////////////////////////
//class PartialTrustHelper

public class Test_000__Helper:MarshalByRefObject
{
 public void TestConnectionOpen(string connectionString)
 {
  // Try to open a connection.
  using (var connection=new System.Data.OleDb.OleDbConnection(connectionString))
  {
   connection.Open();
  }//using cn
 }//TestConnectionOpen
};//class Test_000__Helper

////////////////////////////////////////////////////////////////////////////////
//class Test_000

public static class Test_000
{
 [Test]
 public static void Test_000__NoSqlPermissions()
 {
  //----------------------------------------
  // Create permission set for sandbox AppDomain.
  // This example only allows execution.
  var permissions
   =new PermissionSet(PermissionState.None);

  permissions.AddPermission(new SecurityPermission(SecurityPermissionFlag.Execution));
  
  // Create sandbox AppDomain with permission set that only allows execution,
  // and has no SqlClientPermissions.
  var appDomainSetup
   =new AppDomainSetup();
  
  appDomainSetup.ApplicationBase
   =AppDomain.CurrentDomain.SetupInformation.ApplicationBase;

  var firstDomain
   =AppDomain.CreateDomain("NoSqlPermissions",
                           null,
                           appDomainSetup,
                           permissions);

  // Create helper object in sandbox AppDomain so that code can be executed in that AppDomain.
  var helperType
   =typeof(Test_000__Helper);
  
  var Helper
   =(Test_000__Helper)firstDomain.CreateInstanceAndUnwrap(helperType.Assembly.FullName,
                                                          helperType.FullName);
 
  try
  {
   // Attempt to open a connection in the sandbox AppDomain.
   // This is expected to fail.
   Helper.TestConnectionOpen(TestServices.Conf__GetCnStr());
  }
  catch (System.Security.SecurityException ex)
  {
   Console.WriteLine("Failed, as expected: {0}",ex.FirstPermissionThatFailed);

   // Uncomment the following line to see Exception details.
   Console.WriteLine("BaseException: " + ex.GetBaseException());
  
   return;
  }//catch

  throw new ApplicationException("Connection opened, unexpected.");
 }//Test_000__NoSqlPermissions
};//class Test_000

////////////////////////////////////////////////////////////////////////////////
}//namespace lcpi_data_oledb_tests.OleDbPermission

Все создается, работает и выкидывает исключение SecurityException.

Теперь пытаюсь вместо System.Data.OleDb.OleDbConnection создавать и работать с собственной эквивалентной компонентой, которая живет в отдельной сборке (в ней есть unsafe код и используется CER).
public class Test_001__Helper:MarshalByRefObject
{
 public void TestConnectionOpen(string connectionString)
 {
  //Try to open a connection.
  using (var connection=new lcpi.data.oledb.OleDbConnection(connectionString))
  {
   connection.Open();
  }//using cn
 }//TestConnectionOpen
};//class Test_001__Helper


Она создается (отрабатывает конструктор). Но сваливается внутри Open, который косвенно вызывает RuntimeHelpers.PrepareConstrainedRegions().

lcpi_data_oledb_tests.OleDbPermission.Test_001.Test_001__NoSqlPermissions:
System.MethodAccessException : Методу "lcpi.data.oledb.structure.Structure_ComponentLifeManager.Dispose(lcpi.data.oledb.structure.Structure_IComponentLifeTerminator, Boolean)", прозрачному для безопасности, не удалось получить доступ к критически важному методу безопасности "System.Runtime.CompilerServices.RuntimeHelpers.PrepareConstrainedRegions()".

Сборка "lcpi.data.oledb.net4_5.debug, Version=1.0.0.1533, Culture=neutral, PublicKeyToken=ff716095e8002e7e" является частично доверенной, поэтому среда CLR делает ее полностью прозрачной с точки зрения безопасности независимо от содержащихся в ней аннотаций прозрачности. Для доступа к коду, критическому с точки зрения безопасности, данная сборка должна быть полностью доверенной.


И я уже башку сломал — как заставить этот код работать. Я так понимаю, нужно сделать "lcpi.data.oledb.net4_5.debug" полностью доверенной.

Однако когда я эту сборку указываю в списке fullTrustAssemblies метода CreateDomain, сбой идет при вызове конструктора:

lcpi_data_oledb_tests.OleDbPermission.Test_001.Test_001__NoSqlPermissions:
System.MethodAccessException : Методу "lcpi_data_oledb_tests.OleDbPermission.Test_001__Helper.TestConnectionOpen(System.String)", прозрачному для безопасности, не удалось получить доступ к критически важному методу безопасности "lcpi.data.oledb.OleDbConnection..ctor(System.String)".

Сборка "lcpi.data.oledb.ntest.net4_5.debug, Version=1.0.0.936, Culture=neutral, PublicKeyToken=ff716095e8002e7e" является частично доверенной, поэтому среда CLR делает ее полностью прозрачной с точки зрения безопасности независимо от содержащихся в ней аннотаций прозрачности. Для доступа к коду, критическому с точки зрения безопасности, данная сборка должна быть полностью доверенной.


Если и эту сборку добавить в список fullTrustAssemblies, то получаем исключение из метода CreateInstanceAndUnwrap

lcpi_data_oledb_tests.OleDbPermission.Test_001.Test_001__NoSqlPermissions:
System.Security.SecurityException : Сбой при запросе.


Все сборки (тест и моя компонента) находятся в одном каталоге. Пробовал помещать сборки в GAC — не помогает.

Вообщем, похоже я что-то конкретно не догоняю. И буду признателен за любую полезную подсказку

Если использовать PermissionState.Unrestricted, то вышеперечисленных проблем нет. Но интересует именно работа с PermissionState.None.

Вот полный код, где я мучал свои сборки:
  Скрытый текст
////////////////////////////////////////////////////////////////////////////////
using System;
using System.Reflection;
using System.Security;
using System.Security.Policy;
using System.Security.Permissions;
using NUnit;
using NUnit.Framework;

namespace OleDbPermission{
////////////////////////////////////////////////////////////////////////////////
//class PartialTrustHelper

public class Test_001__Helper:MarshalByRefObject
{
 public void TestConnectionOpen(string connectionString)
 {
  // Try to open a connection.
  using (var connection=new lcpi.data.oledb.OleDbConnection(connectionString))
  {
   connection.Open();
  }//using cn
 }//TestConnectionOpen
};//class Test_001__Helper

////////////////////////////////////////////////////////////////////////////////
//class Test_001

public static class Test_001
{
 //one for all
 private const string c_public_key
  ="002400000480000094000000060200000024000052534131000400000100010031585520470334"
  +"64ce90aec77287bc095f857b499eb5ca5ecfb8a6aaa74c1d1e46cd19f22a92180c33eff2fa5440"
  +"69998755360dbb067548b42857da0c6292b5a64eda7c358c212424f04793b39e0dc48eaa1b01ce"
  +"617f6ef741782d0e05595ddb0068e266e547eff0ca7a09f0c484260005a27fe2bb2b07ac359f15"
  +"e4b3a7b7";

 //-----------------------------------------------------------------------
 private static byte ToHex(char c)
 {
  if(c>='0' && c<='9')
   return (byte)(c-'0');

  if(c>='A' && c<='F')
   return (byte)((c-'A')+10);

  if(c>='a' && c<='f')
   return (byte)((c-'a')+10);

  throw new ApplicationException("Bad HEX");
 }//ToHex
 
 //-----------------------------------------------------------------------
 private static byte[] get_public_key()
 {
  var result=new byte[c_public_key.Length/2];

  for(int i=0,x=0,_c=c_public_key.Length;i!=_c;i+=2,++x)
  {
   result[x]=(byte)(16*ToHex(c_public_key[i])+ToHex(c_public_key[i+1]));
  }

  return result;
 }//get_public_key

 //-----------------------------------------------------------------------
 [Test]
 public static void Test_001__NoSqlPermissions()
 {
  //----------------------------------------
  // Create permission set for sandbox AppDomain.
  // This example only allows execution.
  var permissions
   =new PermissionSet(PermissionState.None);

  permissions.AddPermission(new SecurityPermission(SecurityPermissionFlag.AllFlags));
  
  // Create sandbox AppDomain with permission set that only allows execution,
  // and has no SqlClientPermissions.
  var appDomainSetup
   =new AppDomainSetup();
  
  appDomainSetup.ApplicationBase
   =AppDomain.CurrentDomain.SetupInformation.ApplicationBase;

  //----------------------------------------
  var skey=new StrongNamePublicKeyBlob(get_public_key());

  //lcpi.data.oledb.net4_5.debug.dll
  var a1_name=new AssemblyName(typeof(lcpi.data.oledb.OleDbConnection).Assembly.FullName);
  
  //lcpi.lib.net4_5.debug.dll               HELPER ASSEMBLY
  var a2_name=new AssemblyName(typeof(lcpi.lib.com.ClsCtxCode).Assembly.FullName);

  //lcpi.data.oledb.ntest.net4_5.debug.pdb  OUR ASSEMBLY
  var a3_name=new AssemblyName(typeof(Test_001__Helper).Assembly.FullName);

  //-----
  var a1_sname=new StrongName(skey,
                              a1_name.Name,
                              a1_name.Version);

  var a2_sname=new StrongName(skey,
                              a2_name.Name,
                              a2_name.Version);

  var a3_sname=new StrongName(skey,
                              a3_name.Name,
                              a3_name.Version);

  //----------------------------------------
  var firstDomain
   =AppDomain.CreateDomain("NoSqlPermissions",
                           null,
                           appDomainSetup,
                           permissions,
                           a1_sname,    
                           //a2_sname,
                           a3_sname);

  // Create helper object in sandbox AppDomain so that code can be executed in that AppDomain.
  var helperType
   =typeof(Test_001__Helper);
  
  var Helper
   =(Test_001__Helper)firstDomain.CreateInstanceAndUnwrap(helperType.Assembly.FullName,
                                                          helperType.FullName);
 
  try
  {
   // Attempt to open a connection in the sandbox AppDomain.
   // This is expected to fail.
   Helper.TestConnectionOpen(TestServices.Conf__GetCnStr());
  }
  catch (System.Security.SecurityException ex)
  {
   Console.WriteLine("Failed, as expected: {0}",ex.FirstPermissionThatFailed);

   // Uncomment the following line to see Exception details.
   Console.WriteLine("BaseException: " + ex.GetBaseException());
  
   return;
  }//catch

  throw new ApplicationException("Connection opened, unexpected.");
 }//Test_001__NoSqlPermissions
};//class Test_001

////////////////////////////////////////////////////////////////////////////////
}//namespace OleDbPermission
-- Пользователи не приняли программу. Всех пришлось уничтожить. --
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.