понедельник, 9 мая 2011 г.

Чудеса аутентификации. Чудо второе: доступ запрещён

Допустим, вы пишете клиент-серверное приложение, причём серверная часть состоит из нескольких компонентов. После получения запроса, сервис, реализующий внешний интерфейс, выполняет олицетворение (impersonation) клиента и отправляет запросы к остальным частям сервера по мере необходимости. Рассмотрим типичную ситуацию: всё прекрасно работало, пока серверные компоненты были развёрнуты на одной машине в вашем тестовом окружении, но как только сервер попал в отдел контроля качества, и его части оказались на разных машинах (он стал действительно распределённым), клиент начал получать ошибку "Access denied". Даже программисты с хорошим опытом зачастую попадают в подобную ловушку и не могут сразу ответить, что же идёт не так.

Impersonation vs Delegation
Типичным примером описанной проблемы является ошибка отказа доступа при попытке обращения сервера от имени клиента к сервисам Active Directory через ADSI или .NET классы из пространства имён System.DirectoryServices, которые являются всего лишь тонкой обёрткой вокруг всё того же ADSI. Всё работает до той поры, пока сервер запускается на контроллере домена. Как только он оказывается на любой другой машине домена, всё разваливается на глазах.

Данная проблема хорошо известна под названием "Проблемы двойного прыжка" или "Double hop problem". Дело в том, что существует два уровня олицетворения клиента: собственно олицетворение (impersonation) и делегирование (delegation). Первый уровень олицетворения позволяет серверу выполнять локальные операции от имени клиента. И только делегирование позволяет серверу представляться клиентом при выполнении удалённых операций. Поскольку обращение к сервисам Active Directory через ADSI подразумевает удалённый вызов (это не что иное, как обращение к DCOM объектам), то все попытки сервера осуществить подобный вызов от имени клиента при отключенной возможности делегирования завершатся с ошибкой "Access denied".

Делегирование возможно при соблюдении следующих условий:
  1. В домене используется протокол аутентификации Kerberos.
  2. Клиент явно разрешает серверу использовать свой маркер для делегирования. В случае DCOM, например, это можно сделать, с помощью функций CoInitializeSecurity или CoSetProxyBlanket (в обоих случаях достигается передачей значения RPC_C_IMP_LEVEL_DELEGATE через атрибут dwImpLevel).
  3. Учётная запись пользователя не отмечена в Active Directory как "Account is sensitive and cannot be delegated".
  4. Учётная запись сервера отмечена в Active Directory как "Trust computer for delegation" (настройки доступны на вкладке Delegation свойств хоста в Active Directory).

Kerberos и делегирование
Делегирование возможно только в рамках применения протокола Kerberos - это единственный провайдер в ОС Windows, поддерживающий данный тип олицетворения. Необходимо отметить, что в числе настроек "Constrained delegation" (вкладка Delegation свойств хоста), есть возможность выбрать опцию "Use any authentication protocol". Эта опция означает использование преобразование протоколов (protocol transition) и подразумевает преобразование любого протокола аутентификации, используемого клиентом, в протокол Kerberos.
Требование Kerberos возникло не случайно. Делегирование полностью основано на особенностях данного протокола. Постараюсь кратко изложить его суть. Протокол Kerberos является частным случаем протокола Нидхема - Шрёдера, он состоит из нескольких фаз:
  1. Аутентификация в домене и получение Билета для получения билетов (Ticket Granting Ticket - TGT).
  2. Получение Билета для доступа к сервису (Client to Server Ticket - CST).
  3. Обращение к сервису.

Билет является основополагающим элементом в протоколе Kerberos. Он выдаётся клиенту, для дальнейшего предоставления удалённому сервису. Каждый билет, в том числе и Билет для получения билетов (TGT), содержит в открытом виде домен и имя пользователя, которому данный билет был предоставлен. Помимо открытых полей он содержит следующие поля, зашифрованные секретным ключом того сервиса, которому билет адресован:
  • флаги;
  • сессионный ключ;
  • домен и имя пользователя (копия открытых полей);
  • время действия билета (начало и конец);
  • адрес клиентского хоста, которому был выдан билет;
  • данные для авторизации (например, перечень групп, в которые входит данный пользователь).

В качестве секретного ключа клиента используется хэш пароля, вычисленный операционной системой при входе пользователя на клиентскую машину. Билеты выдаются Центром Распространения Ключей (Key Distribution Center - KDC), роль которого исполняет контроллер домена. Ему известны секретные ключи всех серверов и хэши паролей всех пользователей домена.

Получение TGT
  1. Клиентская операционная система отправляет запрос на получение TGT, содержащий учётные данные пользователя, сразу после ввода пользователем пароля при входе в систему.
  2. KDC генерирует сессионный ключ Kck для обмена сообщениями между клиентом и KDC и записывает его в формируемый билет TGT. Закрытые поля TGT шифруются своим секретным ключом KDC. Ключ Kck шифруется хэшем пароля пользователя, который имеется в наличии у KDC, и данная информация вместе с TGT отправляется клиенту.
  3. Клиент расшифровывает сессионный ключ Kck своей копией хэша пароля и запоминает его вместе с TGT.

Получение CST
  1. Клиент отправляет KDC следующие сообщения: TGT и идентификатор сервиса, к которому необходим доступ; аутентификатор клиента (идентификатор клиента и временной штамп), зашифрованный ключом Kck.
  2. KDC расшифровывает TGT своим ключом, извлекает из него сессионный ключ Kck; расшифровывает аутентификатор клиента, сравнивает IP адрес клиента с IP адресом из TGT, проверяет временной штамп (он должен отличаться от текущего времени KDC не более чем на 5 минут).
  3. Если все проверки успешны, KDC берёт идентификатор запрашиваемого сервера, определяет его секретный ключ, генерирует сессионный ключ Kcs для обмена сообщениями между клиентом и сервером и прописывает его в формируемый билет CST. CST шифруется ключом сервера, Kcs шифруется хэшем пароля клиента и оба сообщения отправляются в ответ на полученный запрос.
  4. Клиент расшифровывает сессионный ключ Kcs своей копией хэша пароля и запоминает его вместе с CST.

Обращение к сервису
  1. Клиент отправляет серверу CST и свой аутентификатор, зашифрованный ключом Kcs.
  2. Сервер расшифровывает CST своим ключом, извлекает из него сессионный ключ Kcs, расшифровывает аутентификатор клиента и проверяет временной штамп. Если требуется взаимная аутентификация, то формирует ответ, содержащий временной штамп клиента +1, зашифрованный сессионным ключом Kcs.
  3. Клиент проверяет ответ сервера, и соединение считается установленным. В дальнейшем сессионный ключ Kcs используется для шифрования и / или подписания сообщений между клиентом и сервером.

Итак, где же в этой цепочке появляется делегирование? Если все условия для делегирования выполнены, то KDC, выписывая клиенту TGT, добавляет к его флагам ключ FORWARDABLE. Если клиент хочет предоставить серверу право делегирования, вместе с CST он отправляет TGT и ключ Kck зашифрованный ключом Kcs. Сервер использует TGT и ключ Kck для получения CST от имени клиента на доступ к любому удалённому ресурсу. Флаг FORWARDABLE, включённый в состав TGT, информирует KDC о необходимости пропустить проверку IP адресов. Полученный таким образом билет сервер использует для доступа к нужному ресурсу так, как будто бы аутентификацию прошёл сам клиент.

Комментариев нет:

Отправить комментарий