Сущность технологии СОМ. Библиотека программиста
Сущность технологии СОМ. Библиотека программиста читать книгу онлайн
В этой книге СОМ исследуется с точки зрения разработчика C++. Написанная ведущим специалистом по модели компонентных объектов СОМ, она раскрывает сущность СОМ, помогая разработчикам правильно понять не только методы модели программирования СОМ, но и ее основу. Понимание мотивов создания СОМ и ее аспектов, касающихся распределенных систем, чрезвычайно важно для тех разработчиков, которые желают пойти дальше простейших приложений СОМ и стать по-настоящему эффективными СОМ-программистами. Показывая, почему СОМ для распределенных систем (Distributed СОМ) работает именно так, а не иначе, Дон Бокс дает вам возможность применять эту модель творчески и эффективно для ежедневных задач программирования.
Внимание! Книга может содержать контент только для совершеннолетних. Для несовершеннолетних чтение данного контента СТРОГО ЗАПРЕЩЕНО! Если в книге присутствует наличие пропаганды ЛГБТ и другого, запрещенного контента - просьба написать на почту [email protected] для удаления материала
if (SUCCEEDED(hr)) {
// build list of users/rights using NT4 security data types
// создаем списов пользователей/прав, используя типы данных защиты из NT4
ACTRL_ACCESS_ENTRYW rgaae[] = {
{ { 0, NO_MULTIPLE_TRUSTEE, TRUSTEE_IS_NAME,
TRUSTEE_IS_USER, L"Sales\Bob" },
ACTRL_ACCESS_DENIED, COM_RIGHTS_EXECUTE, 0,
NO_INHERITANCE, 0 },
{ { 0, NO_MULTIPLE_TRUSTEE, TRUSTEE_IS_NAME,
TRUSTEE_IS_GROUP, L"Sales\Managers" },
ACTRL_ACCESS_ALLOWED, COM_RIGHTS_EXECUTE, 0,
NO_INHERITANCE, 0 },
{ { 0, NO_MULTIPLE_TRUSTEE, TRUSTEE_IS_NAME,
TRUSTEE_IS_USER, L"NT AUTHORITY\SYSTEM" },
ACTRL_ACCESS_ALLOWED, COM_RIGHTS_EXECUTE, 0,
NO_INHERITANCE, 0 }
};
ACTRL_ACCESS_ENTRY_LISTW aael =
{ sizeof(rgaae)/sizeof(*rgaae), rgaae };
ACTRL_PROPERTY_ENTRYW ape = { 0, &aael, 0 };
ACTRL_ACCESSW aa = { 1, &ape };
// present list of users+rights to Access Control object
// представляем список пользователей + прав объекту контроля доступа
hr = rpac->SetAccessRights(&aa);
}
return hr;
}
Имея эту функцию, приложение может связать вновь созданный объект контроля доступа с его процессом следующим образом:
IAccessControl *pac = 0;
HRESULT hr = CreateAccessControl(pac);
assert(SUCCEEDED(hr));
hr = CoInitializeSecurity(pac, -1, 0, 0,
RPC_C_AUTHN_LEVEL_PKT, RPC_C_IMP_LEVEL_IDENTIFY, 0,
EOAC_ACCESS_CONTROL,
// use IAccessControl
// используем IAccessControl
0);
assert(SUCCEEDED(hr));
pac->Release();
// COM holds reference until last CoUninitialize
// COM сохраняет ссылку до последнего CoUninitialize
Флаг EOAC_ACCESS_CONTROL показывает, что первый параметр в функции СоInitializeSecurity является указателем на интерфейс IAccessControl, а не указателем на SECURITY_DESCRIPTOR NT. При каждом поступающем запросе на связь COM будет использовать метод этого объекта IsAccessAllowed для определения того, разрешен или запрещен доступ к объектам процесса. Отметим, что хотя этот код должен исполняться до первого интересного вызова COM, вызов CoCreateInstance для получения реализации по умолчанию IAccessControl является допустимым, так как COM не рассматривает его как интересный.
Если список авторизованных пользователей не может быть известен во время запуска процесса, то можно зарегистрировать специальную (custom) реализацию IAccessControl, которая выполняет определенного рода проверку доступа во время выполнения в своей реализации метода IsAccessAllowed. Поскольку сама COM использует только метод IsAccessAllowed, то такая специальная реализация могла бы безошибочно возвращать E_NOTIMPL для всех других методов IAccessControl. Ниже приведена простая реализация IAccessControl, позволяющая получить доступ к объектам процесса только пользователям с символом "x" в именах своих учетных записей:
class XOnly : public IAccessControl {
// Unknown methods
// методы IUnknown
STDMETHODIMP QueryInterface(REFIID riid, void **ppv) {
if (riid == IID_IAccessControl || riid == IID_IUnknown)
*ppv = static_cast<IAccessControl*>(this);
else
return (*ppv = 0), E_NOINTERFACE;
((IUnknown*)*ppv)->AddRef();
return S_OK;
}
STDMETHODIMP_(ULONG) AddRef(void) { return 2; }
STDMETHODIMP_(ULONG) Release(void) { return 1; }
// IAccessControl methods
// методы IAccessControl
STDMETHODIMP GrantAccessRights(ACTRL_ACCESSW *)
{ return E_NOTIMPL; }
STDMETHODIMP SetAccessRights(ACTRL_ACCESSW *)
{ return E_NOTIMPL; }
STDMETHODIMP SetOwner(PTRUSTEEW, PTRUSTEEW)
{ return E_NOTIMPL; }
STDMETHODIMP RevokeAccessRights(LPWSTR, ULONG, TRUSTEEW[])
{ return E_NOTIMPL; }
STDMETHODIMP GetAllAccessRights(LPWSTR, PACTRL_ACCESSW_ALLOCATE_ALL_NODES *,
PTRUSTEEW *, PTRUSTEEW *)
{ return E_NOTIMPL; }
// this is the only IAccessControl method called by COM
// это единственный метод IAccessControl, вызванный COM
STDMETHODIMP IsAccessAllowed(
PTRUSTEEW pTrustee,
LPWSTR lpProperty,
ACCESS_RIGHTS AccessRights,
BOOL *pbIsAllowed)
{
// verify that trustee contains a string
// удостоверяемся, что опекун содержит строку
if (pTrustee == 0 || pTrustee->TrusteeForm != TRUSTEE_IS_NAME)
return E_UNEXPECTED;
// look for X or x and grant/deny based on presence
// ищем "X" или "x" и в зависимости от его наличия
// предоставляем или запрещаем
*pbIsAllowed = wcsstr(pTrustee->ptstrName, L"x") != 0 ||
wcsstr(pTrustee->ptstrName, L"X") != 0;
return S_OK;
}
}
Если экземпляр вышеприведенного класса C++ зарегистрирован c CoInitializeSecurity:
XOnly xo;
// declare an instance of the C++ class
// объявляем экземпляр класса C++
hr = CoInitializeSecurity(static_cast<IAccessControl*>(&xo),
–1, 0, 0, RPC_C_AUTHN_LEVEL_PKT,
RPC_C_IMP_LEVEL_IDENTIFY, 0,
EOAC_ACCESS_CONTROL,
// use IAccessControl
// используем IAccessControl
0);
assert(SUCCEEDED(hr));
то от пользователей, не имеющих "x" в именах своих учетных записей, никакие поступающие вызовы не будут приняты. Поскольку имя опекуна содержит в качестве префикса имя домена, этот простой тест также предоставит доступ учетным записям пользователей, принадлежащих к доменам, содержащим "x" в своих именах. Хотя этот тест доступа вряд ли будет слишком полезен, он демонстрирует технологию использования специального объекта IAccessControl с CoInitializeSecurity.
Управление маркерами
Под Windows NT каждый процесс имеет маркер доступа (access token), представляющий полномочия принципала защиты. Этот маркер доступа создается во время инициализации процесса и содержит различные виды информации о пользователе, в том числе его идентификатор защиты NT (SID), список групп, к которым принадлежит пользователь, а также список привилегий, которыми он обладает (например, может ли пользователь прекращать работу системы, может ли он менять значение системных часов). Когда процесс пытается получить доступ к ресурсам ядра безопасности (например, к файлам, ключам реестра, семафорам), контрольный монитор защиты NT (SRM – Security Reference Monitor) использует маркер вызывающей программы в целях аудита (отслеживания действий пользователей путем записи в журнал безопасности выбранных типов событий безопасности) и контроля доступа.
Когда в процесс поступает сообщение об ORPC-запросе, COM организует выполнение вызова соответствующего метода или в RPC-потоке (в случае объектов, расположенных в МТА), или в потоке, созданном пользователем (в случае объектов, расположенных в STA). В любом случае метод выполняется с использованием маркера доступа, соответствующего данному процессу. В целом этого достаточно, так как это позволяет разработчикам объекта прогнозировать, какие привилегии и права будут иметь их объекты, независимо от того, какой пользователь осуществляет запрос. В то же время иногда бывает полезно, чтобы метод выполнялся с использованием прав доступа клиента, вызывающего метод; чтобы можно было либо ограничить, либо усилить обычные права и привилегии объекта. Для поддержки такого стиля программирования в Windows NT допускается присвоение маркеров защиты отдельным потокам. Если поток имеет свой собственный маркер, контрольный монитор защиты не использует маркер процесса. Вместо него для выполнения аудита и контроля доступа используется маркер, присвоенный потоку. Хотя есть возможность программно создавать маркеры и присваивать их потокам, в COM предусмотрен гораздо более прямой механизм создания маркера на основе ORPC-запроса, обслуживаемого текущим потоком. Этот механизм раскрывается разработчикам объекта посредством контекстного объекта вызова, то есть вспомогательного объекта, который содержит информацию об операционном окружении серверного объекта.