Добавить в цитаты Настройки чтения

Страница 200 из 372

Исполняемое серверное приложение выступает в роли принципала со своими именем, паролем, SID. Ранее (в СОМ) серверное приложение могло запускаться под идентификацонными данными запустившего его клиента, но такое решение было признано немасштабируемым. В СОМ+ администратор выбирает один из двух вариантов:

♦ Интерактивный пользователь

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

♦ Заданный пользователь

В этом случае администратор задает для конкретного серверного приложения уникальные имя и пароль, регистрируя приложение как нового пользователя в системе с определенными правами. Передача прав клиента серверному приложению возможна через механизм имперсонализации.

• Роли

В СОМ+ механизм ролей используется при авторизации клиентов. Авторизация клиента означает выяснение его прав, связанных с запуском серверного приложения, с вызовом того или иного метода серверного объекта. При конфигурировании серверного приложения администратор определяет список ролей, которым могут принадлежать клиенты. Далее к каждой роли приписываются те или иные пользователи, зарегистрированные в домене. Удобно приписывать к одной роли целую группу пользователей (с заданным SID). Одной роли можно приписать многих пользователей, и один пользователь может исполнять несколько ролей. Классические примеры ролей: клерк, менеджер.

После распределения пользователей по ролям (относительно данного серверного приложения), администратор может приписать определенные роли приложению в целом, отдельным его компонентам, интерфейсам и методам. В результате, доступ к некоторому ресурсу (компоненту, интерфейсу, методу) получают только те клиенты, которые выполняются с 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. Если был использован второй флаг, то прокси делает все проходящие через него вызовы от лица клиента, данные которого записаны в текущий маркер доступа процесса. Это означает, что, например, чередуя вызовы