Сущность технологии СОМ. Библиотека программиста

ОглавлениеДобавить в закладки К обложке

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 может быть успешным, даже если обычно процесс объекта не имеет доступа к этому файлу.

Важно, что хотя методы IServerSecurity::ImpersonateClient всегда достигают цели, исключая катастрофический сбой, клиент объекта контролирует уровень заимствования прав, допускаемый результирующим маркером. Каждый интерфейсный заместитель имеет свой уровень заимствования прав, который должен быть равным одной из четырех констант (RPC_C_IMP_LEVEL_ANONYMOUS, RPC_C_IMP_LEVEL_IDENTIFY, RPC_C_IMP_LEVEL_IMPERSONATE или RPC_C_IMP_LEVEL_DELEGATE). Во время демаршалинга COM устанавливает этот уровень равным величине, определенной в клиентском вызове CoInitializeSecurity; однако данная установка может быть изменена вручную с помощью IClientSecurity::SetBlanket. Когда объект вызывает IServerSecurity::ImpersonateClient, новый маркер будет ограничен уровнем, заданном в интерфейсном заместителе, который использовался в данном вызове. Это означает, что если клиент задал только уровень RPC_C_IMP_LEVEL_IDENTIFY, то объект не может получить доступ к ресурсам ядра во время выполнения с полномочиями клиента. Объект, однако, может применить API-функции Win32 OpenThreadToken или GetTokenInformation для чтения информации о клиенте (например, ID защиты, групповое членство) из маркера режима анонимного воплощения (impersonation token). Важно отметить, что пока клиент не задал уровень RPC_C_IMP_LEVEL_DELEGATE, объект не может получить доступ ни к одному из удаленных ресурсов защиты, используя полномочия клиента. В их число входят открытие файлов в удаленной файловой системе, а также выполнение аутентифицированных COM-вызовов к удаленным объектам. К сожалению, протокол аутентификации NTLM не поддерживает уровень RPC_C_IMP_LEVEL_DELEGATE, так что под Windows NT 4.0 делегирование невозможно.

Во время предыдущего обсуждения акцент делался на том, что в нормальном режиме методы объекта выполняются с использованием маркера доступа процесса объекта. Однако не обсуждался вопрос о том, как проконтролировать, какой принципал защиты должен использоваться для создания начального маркера серверного процесса. Когда SCM запускает серверный процесс, то он присваивает новому серверному процессу маркер, основанный на конфигурации именованной величины RunAs из AppID. Если же в AppID нет величины RunAs, то считается, что сервер неправильно сконфигурирован для работы в режиме распределенного доступа. Для того чтобы этот тип серверного процесса не внедрял указанные «дыры» в защите в систему, SCM запускает такие процессы с использованием того принципала защиты, который произвел запрос на активацию. Такой тип активации часто называют активацией «как активизатор» («As Activator»), так как серверный процесс выполняет тот же принципал защиты, что и запускающий пользователь. Активация типа «как активизатор» предназначена для поддержки удаленной активации старых серверов и содержит несколько ловушек. Во-первых, чтобы придерживаться семантики типа «как активизатор», COM запустит отдельный серверный процесс для каждой активационной учетной записи пользователя, независимо от того, используется ли REGCLS_MULTIPLEUSE в CoRegisterClassObject. Это вступает в серьезный конфликт с принципом расширяемости и вдобавок делает невозможным сохранение всех экземпляров класса в одном и том же процессе. Во-вторых, каждый серверный процесс запускается с маркером, ограниченным уровнем RPC_C_IMP_LEVEL_IMPERSONATE, из чего следует, что серверные процессы не имеют доступа ни к каким удаленным ресурсам или объектам[1].


Логин
Пароль
Запомнить меня