Когда SCM решает, что должен быть запущен новый серверный процесс, он пытается получить дескриптор защиты NT SECURITY_DESCRIPTOR, описывающий, каким пользователям разрешено запускать серверный процесс. В первую очередь SCM проверяет AppID класса для явной установки прав запуска. Эта установка приходит в форме сериализованного дескриптора защиты NT, который хранится в именованной величине LaunchPermission AppID:
[HKCR\AppID\{27EE6A4D-DF65-1d0-8C5F-0080C73925BA}]
LaunchPermission=
Если эта именованная величина отсутствует, SCM пытается прочитать общие для всей машины права запуска из такой именованной величины:
[HKEY_LOCAL_MACHINE\Software\Microsoft\OLE]
DefaultLaunchPermission=
Обе эти установки могут быть модифицированы с помощью DCOMCNFG.EXE. Если не найден ни один из этих ключей реестра, то COM запретит запуск кому бы то ни было. Если же SECURITY_DESCRIPTOR найден, SCM проверяет идентификатор защиты активизирующей вызывающей программы (формально называемой активизатором – actiuator) по списку разграничительного контроля доступа DACL (Discretionary Access Control List), имеющемуся в дескрипторе, чтобы определить, имеет ли активизатор полномочия на запуск сервера. Если активизатор не имеет необходимых полномочий, то следует отказ на активационный вызов с HRESULT E_ACCESSDENIED, и никаких процессов не запускается. В случае успешной проверки SCM запускает серверный процесс и продолжает выполнение активационного запроса.
Права запуска определяют только, какие пользователи могут или не могут начинать серверные процессы во время активации. Эта проверка всегда выполняется SCM на основе информации, записанной в реестре. Права доступа определяют, какие пользователи могут действительно связываться с объектами серверного процесса. Эта проверка осуществляется библиотекой COM при каждом запросе на установку соединения, приходящем от клиента. Для контроля установок прав доступа к процессу разработчики могут использовать API-функцию CoIntializeSecurity.
Напомним, что процессы, не вызывающие явно функцию CoInitializeSecurity, автоматически используют список контроля доступа, записанный под ключом реестра AppID приложения:
[HKCR\AppIO\{27EE6A4D-DF65-11d0-8C5F-0080C73925BA}]
AccessPermission=
Ранее объяснялось, что если этот элемент реестра отсутствует, то COM ищет установку по умолчанию для всей машины, а если она также отсутствует, то создается новый список контроля доступа, который включает только принципала серверных процессов и встроенной учетной записи SYSTEM.
Приложения, явно вызывающие CoInitializeSecurity, могут вручную контролировать, каким вызывающим программам разрешен доступ к объектам, экспортируемым данным процессом. По умолчанию первый параметр CoIntializeSecurity принимает указатель на SECURITY_DESCRIPTOR NT. Если вызывающая программа передает в качестве этого параметра нулевой указатель, то COM не будет осуществлять никакого контроля входящих вызовов. Это разрешает вызовы от любого аутентифицированного принципала защиты. Если и клиент, и сервер укажут RPC_C_AUTHN_LEVEL_NONE, то COM разрешит вызовы от кого угодно, независимо от его аутентификации. Если же в вызывающей программе имеется легальный указатель на дескриптор защиты, то COM с помощью DACL этого дескриптора защиты определит, каким вызывающим программам разрешен доступ к объектам процесса. Заголовки SDK определяют так называемый флаг прав (COM_RIGHTS_EXECUTE), который используется при создании DACL для явного разрешения или запрета пользователям на связь с объектами процесса.
Хотя и допускается использовать API-функции Win32 для создания SECURITY_DESCRIPTOR с целью передачи его в CoInitializeSecurity, этот способ контроля доступа к объектам процесса не является предпочтительным, в основном по причине темной природы API-функций защиты Win32. Для упрощения программирования в COM контроля доступа в реализации COM для Windows NT 4.0 Service Pack 2 разработчикам разрешено указывать тот объект COM, который будет использоваться для выполнения проверки доступа при установке новых соединений. Этот объект регистрируется библиотекой COM во время выполнения CoInitializeSecurity и должен реализовать интерфейс IAccessControl:
[object, uuid(EEDD23EO-8410-11CE-A1C3-08002B2B8D8F)]
interface IAccessControl : IUnknown {
// add access allowed rights for a list of users
// добавляем разрешенные права доступа для списка пользователей
HRESULT GrantAccessRights([in] PACTRL_ACCESSW pAccessList);
// explicitly set the access rights for a list of users
// явно устанавливаем права доступа для списка пользователей
HRESULT SetAccessRights([in] PACTRL_ACCESSW pAccessList
// users+rights
// пользователи + Права
);
// set the owner/group IDs of the descriptor
// устанавливаем идентификаторы владельца/группы для дескриптора
HRESULT Set0wner(
[in] PTRUSTEEW pOwner, // owner ID
// ID владельца
[in] PTRUSTEEW pGroup // group ID
// ID группы
);
// remove access rights for a list of users
// удаляем права доступа для списка пользователей
HRESULT RevokeAccessRights(
[in] LPWSTR lpProperty, // not used
// не используется