Выбрать главу

После распределения пользователей по ролям (относительно данного серверного приложения), администратор может приписать определенные роли приложению в целом, отдельным его компонентам, интерфейсам и методам. В результате, доступ к некоторому ресурсу (компоненту, интерфейсу, методу) получают только те клиенты, которые выполняются с SID пользователя, исполняющего соответствующую роль.

Приписывание роли некоторому ресурсу означает, что эта же роль приписывается и всем ресурсам, входящим в данный ресурс. Например, приписывание некоторой роли всему приложению означает, что всем его компонентам, их интерфейсам и методам приписана эта же роль. Это ограничивает возможность более тонкого контроля доступа (программируемого контроля) к компонентам приложения. Следует приписывать роли отдельным компонентам, интерфейсам и методам. В этом случае разнообразная связанная с безопасностью информация будет включена в контекст соответствующих объектов, что позволит выполнять программный контроль за доступом.

В связи с рассмотренным только что вопросом о ролях уместно более подробно остановиться на программируемом контроле доступа к приложению. Механизм ролей позволяет разрешить или запретить вызов некоторого метода некоторого серверного объекта всем клиентам, исполняющим определенную роль. Однако, в некоторых случаях желательно иметь более тонкий контроль на доступом, учитывающий не только роль клиента, но и значения параметров, которые этот клиент задал при вызове метода. В этом случае не обойтись без программируемого контроля на стороне сервера.

С каждым вызовом некоторого метода серверного объекта связан так называемый контекст безопасности вызова (конечно, для этого должны быть заданы роли, и некоторая роль должна быть приписана этому методу или содержащему его интерфейсу, компоненту, но не всему приложению). Имеется интерфейс ISecurityCallContext, через который можно получить доступ к этому контексту. Для получения указателя на этот интерфейс можно вызвать из потока, исполняющего данный метод, функцию CoGetCallContext. В качестве входного параметра задается идентификатор запрашиваемого интерфейса (ID_ISecurityCallContext), через выходной параметр возвращается указатель на этот интерфейс.

Для нашей задачи контроля доступа к методу в зависимости от параметров можно использовать следующие методы данного интерфейса:

• IsSecurity Enabled возвращает как выходной параметр логическое значение: TRUE, если основанная на ролях система безопасности задействована, и FALSE в противном случае.

• Если система безопасности задействована, то метод IsCalleIinRole используется для проверки принадлежности клиента, вызвавшего метод, заданной роли. Входной параметр задает строку с ролью, выходной возвращает истину или ложь в зависимости от принадлежности клиента специфицированной роли. В зависимости от этого значения можно продолжить выполнение основной логики метода, либо прервать его выполнение с соответствующим кодом ошибки.

Вообще, интерфейс ISecurityCallContext позволяет получить следующую информацию об исполняемом вызове:

• Длина цепи вызовов

Как уже отмечалось ранее, в СОМ каждый вызов получает уникальный идентификатор, и все последующие вызовы, сделанные ради выполнения данного вызова, получают тот же идентификатор. Это позволяет отслеживать цепочки вызовов.

• Минимальный уровень аутентификации, использующийся в данной цепи вызовов

• Идентификационная информация для всех клиентов, сделавших включенные в данную цепь вызовы:

♦ SID

♦ Имя

♦ Сервис аутентификации (например, Kerberos)

♦ Уровни аутентификации и имперсонализации

• Идентификационные данные клиента, сделавшего первый вызов в цепи

• Идентификационные данные клиента, сделавшего последний вызов в цепи

Теперь рассмотрим, как на стороне сервера решается вопрос имперсонализации клиента и делегирование его прав при удаленном вызове.

С помощью функции CoGetCallContext можно получить также указатель на интерфейс IServerSecurity. Именно этот интерфейс предоставляет метод без параметров ImpersonateClient, который при вызове в потоке, выполняющем вызов клиента, имперсонирует этого клиента, приписывая потоку маркер доступа с аутентификационными данными этого клиента. Вызов другого метода без параметров этого же интерфейса — RevertToSeif позволяет вернуть потоку исходный маркер доступа.

Как уже отмечалось ранее, если уровень аутентификации клиента был установлен не ниже чем в Impersonate, то после имперсонализации клиента сервер получает доступ (на время выполнения этого метода или до вызова RevertToSelf) ко всем локальным ресурсам от имени клиента пользуясь всеми его правами. Однако, если уровень имперсонализации установлен в Delegate, можно было бы ожидать, что данный поток получает право делать от лица клиента и удаленные вызовы. Однако, это не так. Ради совместимости с предыдущими версиями (СОМ под Windows NT), делегирование будет иметь место только после дополнительной настройки. Именно, после имперсонализации клиента С поток сервера S должен выполнить cloakig, т. е. скрыть свою сущность от вызываемого сервера Q под маской имперсонированного клиента С. Один из способов состоит в задании специального флага для прокси, который поток сервера S получил, выполнив вызов сервера Q. Для этого можно получить текущие установки уровня безопасности для данного прокси используя IClientSecurity::GetBlanket, добавить один из следующих флагов:

• EOAC_STATIC_CLOAKING

• EOAC_DYNAMIC_CLOAKING

и задать новые установки для прокси с помощью IClientSecurity::SetBlanket.

Упомянутые выше флаги имеют следующий смысл. При задании первого флага все последующие вызовы через этот прокси будут делаться от лица того клиента, который был имперсонирован до вызова SetBlanket. Если был использован второй флаг, то прокси делает все проходящие через него вызовы от лица клиента, данные которого записаны в текущий маркер доступа процесса. Это означает, что, например, чередуя вызовы

IServerSecurity::ImpersonateClient и IServerSecurity::RevertToSeif, текущий поток будет делать удаленные вызовы то от имени клиента С, то от имени сервера S.

Асинхронные компоненты (Queued Components)

Вначале несколько слов о терминологии. Термин Queued Components сложно перевести на русский язык, сохраняя стоящий за ним (в рамках СОМ+) смысл. Дословный перевод "организованные в очередь компоненты", который иногда используется в русскоязычной литературе, заставляет предполагать, что имеется в виду некоторая очередь различных компонентов, тогда как все совершенно иначе — имеется очередь вызовов к одному компоненту. В данном курсе выбран термин "асинхронные компоненты", который должен подчеркивать возможность коммуникации клиента и сервера, функционирующих в различные, непересекающиеся интервалы времени.

Здесь же необходимо отметить, что в СОМ+ появилась новая возможность объявить некоторый интерфейс как асинхронный. Асинхронные интерфейсы и асинхронные компоненты (Queued Components) — это разные технологии. Использование асинхронного интерфейса позволяет клиенту сделать вызов некоторого метода асинхронного интерфейса сервера и сразу же заняться чем-нибудь еще (при использовании обычного синхронного интерфейса клиент блокируется до получения ответа от сервера). Но при этом и клиент и сервер должны функционировать одновременно. В случае вызова, направленного асинхронному компоненту, клиент также не блокируется, как и при вызове асинхронного интерфейса. И, кроме того, клиент может делать вызов сервера в тот момент, когда сервер еще не запущен.