Сущность технологии СОМ. Библиотека программиста - Дональд Бокс
- Дата:20.06.2024
- Категория: Компьютеры и Интернет / Программирование
- Название: Сущность технологии СОМ. Библиотека программиста
- Автор: Дональд Бокс
- Просмотров:3
- Комментариев:0
Шрифт:
Интервал:
Закладка:
Если список авторизованных пользователей не может быть известен во время запуска процесса, то можно зарегистрировать специальную (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-запроса, обслуживаемого текущим потоком. Этот механизм раскрывается разработчикам объекта посредством контекстного объекта вызова, то есть вспомогательного объекта, который содержит информацию об операционном окружении серверного объекта.
Напоминаем, что контекстный объект вызова сопоставляется с потоком, когда ORPC-запрос направляется на интерфейсную заглушку. Разработчики объекта получают доступ к контексту вызова через API-функцию CoGetCallContext. Контекстный объект вызова реализует интерфейс IServerSecurity:
[local, object, uuid(0000013E-0000-0000-C000-000000000046)]
interface IServerSecurity : IUnknown {
// get caller's security settings
// получаем установки защиты вызывающей программы HRESULT
QueryBlanket(
[out] DWORD *pAuthnSvc, // authentication pkg
// модуль аутентификации
[out] DWORD *pAuthzSvc, // authorization pkg
// модуль авторизации
[out] OLECHAR **pServerName, // server principal
// серверный принципал
[out] DWORD *pAuthnLevel, // authentication level
// уровень аутентификации
[out] DWORD *pImpLevel, // impersonation level
// уровень заимствования прав
[out] void **pPrivs, // client principal
// клиентский принципал
[out] DWORD *pCaps // EOAC flags
// флаги EOAC
);
// start running with credentials of caller
// начинаем выполнение с полномочиями вызывающей программы
HRESULT ImpersonateClient(void);
// stop running with credentials of caller
// заканчиваем выполнение с полномочиями вызывающей программы
HRESULT RevertToSelf(void);
// test for impersonation
// проверка заимствования прав
BOOL IsImpersonating(void);
}
В одном из предыдущих разделов этой главы уже рассматривался метод QueryBlanket. Остальные три метода используются для управления маркерами потока во время выполнения метода. Метод ImpersonateClient создает маркер доступа, основанный на полномочиях клиента, и присваивает этот маркер текущему потоку. Как только возвращается IServerSecurity::ImpersonateClient, все попытки доступа к ресурсам операционной системы будут разрешаться или запрещаться в соответствии с полномочиями клиента, а не объекта. Метод RevertToSelf заставляет текущий процесс вернуться к использованию маркера доступа, принадлежащего процессу. Если текущий вызов метода заканчивает работу во время режима заимствования прав, то COM неявно вернет поток к использованию маркера процесса. И наконец, метод IServerSecurity::IsImpersonating показывает, что использует текущий поток: полномочия клиента или маркер процесса объекта. Подобно методу QueryBlanket, два метода IServerSecurity также имеют удобные оболочки, которые вызывают CoGetCallContext изнутри и затем вызывают соответствующий метод:
HRESULT CoImpersonateClient(void);
HRESULT CoRevertToSelf(void);
В общем случае, если будет использоваться более одного метода IServerSecurity, то эффективнее было бы вызвать CoGetCallContext один раз, а для вызова каждого метода использовать результирующий интерфейс IServerSecurity.
Следующий код демонстрирует использование контекстного объекта вызова для выполнения части кода метода с полномочиями клиента:
STDMETHODIMP MyClass::ReadWrite(DWORD dwNew, DWORD *pdw0ld)
{
// execute using server's token to let anyone read the value
// выполняем с использованием маркера сервера, чтобы
// все могли прочитать данное значение
ULONG cb;
HANDLE hfile = CreateFile(«C:\file1.bin», GENERIC_READ,
0, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
if (hfile == INVALID_HANDLE_VALUE)
return MAKE_HRESULT(SEVERITY_ERROR, FACILITY_WIN32, GetLastError());
ReadFile(hfile, pdwOld, sizeof(DWORD), &cb, 0);
CloseHandle(hfile);
// get call context object
// получаем контекстный объект вызова
IServerSecurlty *pss = 0;
HRESULT hr = CoGetCallContext(IID_IServerSecurity, (void**)&pss);
if (FAILED(hr)) return hr;
// set thread token to use caller's credentials
// устанавливаем маркер потока для использования
// полномочий вызывающей программы
hr = pss->ImpersonateClient();
assert(SUCCEEDED(hr));
// execute using client's token to let only users that can
// write to the file change the value
// выполняем с использованием маркера клиента, чтобы
// изменять это значение могли только те пользователи,
// которые имеют право записывать в файл
hfile = CreateFile(«C:\file2.bin»,
GENERIC_READ | GENERIC_WRITE, 0, 0,
OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
if (hfile == INVALID_HANDLE_VALUE)
hr = MAKE_HRESULT(SEVERITY_ERROR, FACILITY_WIN32, GetLastError());
else {
WriteFile(hfile, &dwNew, sizeof(DWORD), &cb, 0);
CloseHandle(hfile);
}
// restore thread to use process-level token
// восстанавливаем режим использования потоком маркера процесса
pss->RevertToSelf();
// release call context
// освобождаем контекст вызова
pss->Release();
return hr;
}
Отметим, что первый вызов CreateFile выполняется с использованием полномочий процесса объекта, в то время как второй вызов – с полномочиями клиента. Если клиент имеет права доступа для чтения/записи в соответствующий файл, то второй вызов метода CreateFile может быть успешным, даже если обычно процесс объекта не имеет доступа к этому файлу.
- Аквариум. (Новое издание, исправленное и переработанное) - Виктор Суворов (Резун) - Шпионский детектив
- Права на результаты интеллектуальной деятельности и средства индивидуализации: Комментарий к части четвертой Гражданского кодекса Российской Федерации - Вадим Погуляев - Юриспруденция
- У меня есть идея! Что дальше? - Михаил Соболев - О бизнесе популярно
- Ориентирование - К. М. Станич - Современные любовные романы
- Проект 365 - Константин Рочев - Поэзия