Содержание статьи
ISA Server – серьезный компонент обеспечения информационной безопасности.
Сегодня я покажу тебе, как приручить этого монстра и заставить его исполнять
твои желания. Не будем распускать слюни и поминать всуе великий и ужасный
Microsoft. Просто возьмем в руки ISA SDK и реализуем самые, казалось бы,
бредовые идеи.
Если тебе приходилось работать с ISA Server (даже непродолжительное время),
то, наверняка, у тебя сложилось двойственное впечатление об этом программном
продукте. С одной стороны – это мощная система обеспечения сетевой безопасности
и контроля за использованием сетевых ресурсов. Но в нем не хватает многих
вкусностей, которые делают жизнь сисадмина шоколадной. Самый красноречивый
пример – автоматическое управление квотами интернет-трафика. Считать-то трафик
ISA считает, но вот выставить пользователю квоту и перекрыть ему кислород в
случае превышения – мы уже не могем. И приходится вручную просматривать
статистику и ручками отключать нерадивых юзверей от всемирной паутины. А админ,
как известно, существо крайне ленивое и такой расклад его, конечно, не
устраивает.
Нагуглив эту тему, ты получишь десятки ссылок на различные надстройки к Исе.
Все они раздаются не задаром, а за вполне осязаемые американские рубли. Но если
у тебя прямые руки, да еще и растут, откуда надо, то, взяв напильник и рубанок
(в смысле, ISA SDK и MSDN), ты сможешь подогнать Ису под свои потребности, не
заплатив ни рубля софтверным барыгам. А уж что у тебя за потребности – сам
определяй, не маленький. Можешь запретить своим подопечным юзверям убивать
рабочее время в социальных сетях и сэкономить своему работодателю бабла, подняв
производительность офисного планктона на 50%. Можешь хакнуть статистику Исы,
повесив свой трафик на других пользователей. В конце концов, перспектива
порулить файрволом на уровне программных интерфейсов и в обход штатной админки –
заманчивая, не так ли?
Из инструментов тебе понадобятся Visual Studio, сам подопечный файрвол и
ISA
Server SDK. Последний, кстати говоря, ты найдешь на нашем
диске.
ISA-фильтры – быстрый старт
Для начала давай заглянем Исе под капот и посмотрим, как там у нее все
устроено. ISA Server имеет двухуровневую архитектуру – Kernel Mode и User Mode.
Ядро сервера отвечает за взаимодействие с сетевыми интерфейсами компьютера и
выполняет транспортные функции, работая с низкоуровневыми сетевыми протоколами.
Фильтрация сетевого трафика осуществляется на пользовательском уровне – все
пакеты проходят через цепочку фильтров. Каждый фильтр – это отдельная DLL'ка,
выполняющая функции COM-сервера. Фильтры бывают в свою очередь двух типов –
анализирующие сетевой трафик и реагирующие на определенные события. Так как
невозможно объять необъятное, остановимся на фильтрах для Исы первого типа. Уже
реализованные фильтры, которые входят в состав файрвола, соответствуют
практически всем сетевым протоколам прикладного уровня (FTP, SMTP, DNS, POP3,
RPC и т.д.). Ты можешь переопределить их, заменив собственной реализацией,
например, открыть зеленый коридор спам-трафику. К этой же группе относятся
фильтры приложений (пригодятся, если нужно обломать доступ в сеть определенным
прогам) и фильтры сетевых портов (обламывают торрент-сосунов).
А теперь – мат. часть. Расслабься, ее будет совсем немного. Наша цель –
вкурить, каким образом в Исе работают фильтры. Любой фильтр есть не что иное,
как носитель сетевых правил. Правила, в свою очередь, бывают двух типов –
правила доступа и правила публикации сервисов. Другими словами, фильтрующие
входящий трафик и исходящий.
Один сетевой фильтр может объединять в себе несколько правил фильтрации
трафика, относящихся к разным группам.
Идем дальше. Все фильтры могут отрабатывать различные сценарии проверки
сетевых пакетов, основанные на отслеживании используемых протоколов, NAT-сеансов
и фильтрации контента.
Все ISA-фильтры выстраиваются в цепочку. В результате, проходящая через
файрвол информация будет обработана каждым фильтром из цепочки. Организовать
избирательную обработку пакетов какими-либо отдельными фильтрами не получится.
Зато есть возможность выстроить несколько таких цепочек с уникальным набором
фильтров и в различных сценариях пропускать трафик через цепочку, определенную
логикой сетевой защиты.
Фильтруй базар
В момент старта Исы ее основная служба пробегает по списку зарегистрированных
фильтров и для каждого из них вызывает метод IFWXFilter::FilterInit. Он является
точкой входа в фильтр, и метод инициализирует доступные из файрвола глобальные
переменные фильтра. Их основное назначение – зарезервировать за фильтром
определенные сетевые события, при возникновении которых ядро сервера будет
передавать фильтру управление.
// Инициализация сетевого фильтра
STDMETHODIMP CDMFilter::FilterInit
(IFWXFirewall * pIFWXFirewall, FwxFilterHookEvents * pFilterHookEvents)
{
// Сообщаем файрволу, какое именно
событие будем отслеживать
*pFilterHookEvents = m_FwxFilterHookEvents;
return S_OK;
}
После того, как фильтр проинициализирован, за работу принимается обработчик
событий IFWXFilter::AttachToSession – сообщает о каждой новой сессии,
установленной клиентами с Исой. Для сессии соединения клиента с сервером фильтр
создает объект, реализующий интерфейс IFWXSessionFilter, и передает файрволу
ссылку на него.
// Подключаемся к сессии
STDMETHODIMP CDMFilter::AttachToSession
(IFWXSession *piSession,
IFWXSessionFilter **
piSessionFilter, PFwxFilterHookEvents, pFilterHookEvents)
{
HRESULT hr = S_OK;
//Создаем объект, связанный с сессией
CComObject<CDMSessionFilter> *pSessionFilter;
hr = CComObject<CDMSessionFilter>
::CreateInstance(&pSessionFilter);
if (FAILED(hr))
{
return hr;
}
pSessionFilter->AddRef();
// Получаем ссылки на созданные объекты
*pFilterHookEvents = m_FwxFilterHookEvents;
*piSessionFilter = pSessionFilter;
return S_OK;
}
Через этот объект в дальнейшем файрвол и будет общаться с фильтром, посылая
сигналы только о тех событиях, которые его интересуют. Получив сигнал о том, что
в Сети происходит нечто интересное, фильтр создает экземпляр класса
IFWXDataFilter и ассоциирует его с текущим соединением через вызов
IFWXConnection::AttachDataFilter.
В качестве финального аккорда, создадим два сокета (IFWXDataFilter::SetSockets)
и прокачаем данные из точки А в точку B, как учила меня сексапильная математичка
в седьмом классе (упс, Анна Сергеевна, надеюсь вы не читаете журнал «][акер»).
Но об этом ниже.
Из точки А в точку В
В любом ISA-фильтре используется асинхронный ввод/вывод. Фильтр управляет
запросами SEND/RECV через обращения к объекту IFWXSocket, который размещается в
адресном пространстве ядра файрвола и возвращает содержимое I/O-буферов через
интерфейс IFWXIOCompletion.
Получается, процесс передачи сетевых пакетов через фильтр прост, как два
байта об асфальт. Берем указатель на IFWXSocket и отправляем ему RECV-запрос. В
ответ получаем содержимое буфера. Шаманим над ним немного, например,
проанализировав на предмет наличия запрещенного типа данных, отправляем объекту
IFWXSocket запрос SEND и в вдогонку – содержимое I/O буфера. О дальнейшем
позаботится файрвол, точнее, его ядро.
Естественно, чтобы просто получить данные и передать их дальше по сети, много
ума не надо. Да и какой тогда смысл городить весь этот огород? Вот тут-то (между
получением пакетов и их отправкой) и кроется Дао любого ISA-фильтра. Именно в
этом месте канала передачи данных через ISA Server можно творить с сетевыми
пакетами все, что угодно. Что в итоге получит клиент на свой запрос, целиком и
полностью в твоей власти.
Не стану марать попусту бумагу описанием сферического коня в вакууме, – вот
тебе конкретный пример. Предположим, наш фильтр должен передавать пакеты – без
каких-либо изменений, но при этом подробно журналировать: кто, что, кому, и
зачем. Первым делом получаем ссылки на сокеты отправителя и получателя с помощью
простых конструкций типа m_spInternalSocket = piInternalSocket и
m_spExternalSocket = piExternalSocket. Теперь можно создавать асинхронный канал
передачи данных. Делается это с помощью метода, возвращающего в качестве
результата своей работы объект класса STDMETHODIM:
CDMDataFilter::CompleteAsyncIO(). После чего можно прочитать входной буфер:
BYTE* pBuffer = NULL;
DWORD dwBuffSize = 0;
hr = pIOBuffer->GetBufferAndSize
(&pBuffer,&dwBuffSize);
Отправка полученных данных – ничуть не сложнее. Обращаемся к созданному ранее
сокету и сливаем в него содержимое I/O-буфера.
HRESULT hr;
CComPtr<IFWXSocket> spSocket;
GetInternalSocket(&spSocket);
if (spSocket)
{
hr = spSocket->Send(pBuffer,NULL,
ocWriteToInternal);
if (FAILED(hr))
{
return hr;
}
}
return S_OK;
Перед этим мы можем сделать вызов любой другой функции, обрабатывающей
содержимое буфера. Например, hack(pBuffer). Реализация самой функции и
определяет поведение фильтра. В частности, если ты хочешь вести подробный лог о
передаваемых пакетах, можешь воспользоваться представленным в SDK методом
DumpBuffer().
Зарегистрирую. Недорого.
ОК, свой фильтр для Исы мы написали. А как его теперь файрволу подсунуть?
Есть два пути – написать простенький скрипт, осуществляющий регистрацию фильтра,
или предусмотреть механизм регистрации непосредственно в самом фильтре. Во
втором случае регистрация происходит с помощью экспорта функции DllInstall,
вызываемой в момент обращения к regsrv32 с параметром /I.
Строго говоря, чтобы зарегистрировать свой фильтр в Исе, необходимо
одновременное выполнение следующих условий. Во-первых, DLL твоего фильтра должна
находиться на том же компьютере, что и ISA Server. Во-вторых, фильтр должен быть
зарегистрирован как объект конфигурации компьютера. И, в-третьих, он должен быть
зарегистрирован как УЖЕ установленный на компьютер.
Независимо от того, какой способ регистрации ты будешь использовать,
технически она основана на использовании четырех экспортируемых COM-интерфейсов:
- IFWXFilterAdmin – регистрация фильтра;
- FPCEventDefintions – регистрация событий, отслеживаемых фильтром;
- FPCAlerts – регистрация сообщений, генерируемых фильтром;
- FPCApplicationFilter – инициализация значениями по умолчанию.
Чтобы сообщить ядру файрвола, за какими протоколами будет наблюдать
свежеиспеченный фильтр, используется массив: GUID ProtocolsToFilter[] = {SMTP_PROTOCOL_GUID_BIN,
SMTP_SERVER_PROTOCOL_GUID_BIN}. Для описания фильтра используется структура со
следующими полями:
CLSID_DMFilter – GUID
bstrFilterName - Наименование
bstrFilterDescription – Описание
bstrFilterVendor – Разработчик
bstrFilterVersion – Версия
Развеем все возможные вопросы. Вот тебе описание того, как регистрация
фильтра или любого другого расширения для ISA Server выглядит на практике.
Первым делом создается экземпляр COM-объекта FPCWebFilter, входящий в коллекцию
FPCWebFilters. Для идентификации каждого из таких объектов в коллекции
используется глобальный уникальный идентификатор (GUID). Коллекция объектов
FPCWebFilters предоставляет отдельные методы для выполнения следующих операций:
- добавление нового веб фильтра (метод Add);
- изменение порядка следования веб фильтров в коллекции (методы MoveDown и
MoveUp); - удаление веб фильтра из коллекции (метод Remove).
И напоследок – финт ушами в виде ответа на вопрос, который, скорее всего уже
давно засел в твоей голове. Можно ли зарегистрировать фильтр или другое
расширение Исы вручную? Отвечаю – можно. И меня совершенно не интересует, зачем
тебе это нужно :).
Наша задача сводится к тому, чтобы поместить запись о новом фильтре в
конфигурационный массив Исы. Как ты уже знаешь, с точки зрения внутренней
организации файрвола, это будет выглядеть как добавление нового FPCWebFilter
объекта в коллекцию FPCWebFilters. Следовательно, нужно как-то добраться до этой
самой коллекции. Порывшись в документации, я выяснил, как это можно реализовать.
Следи за цепочкой. Конфигурационный массив сервера доступен для изменения извне.
В массиве содержится ссылка на объект класса FPCExtensions. У этого объекта есть
несколько публичных полей. Одно из таких полей имеет название WebFilters, и, как
нетрудно догадаться, представляет собой ссылку на коллекцию FPCWebFilters. На
уровне операционной системы эта коллекция представлена как обычная ветка
реестра. Означает это, что, добавив буквально три строки в код нашего расширения
для ISA Server, мы сможем вручную его зарегистрировать (тривиальным способом с
помощью regsrv32). Для этого добавляем к нашему фильтру небольшую функцию
следующего вида:
STDAPI DllRegisterServer(void)
{
HRESULT hr = RegisterWebFilter(true);
return FAILED(hr) ? S_FALSE : S_OK;
}
Теперь ты можешь подключить свой фильтр к работающей на полном ходу Исе без
каких-либо вопросов с ее стороны. А дальше – уже дело твоей фантазии.
Лирическо-спортивное
Обычно на этом месте авторы по всем правилам жанра подводят итог своему
повествованию, резюмируя наиболее важные моменты, делая умные и пространные
выводы. Сегодня мне хотелось бы немного отойти от традиций. Статья, которую ты
только что прочитал, писалась за несколько дней до начала Пекинской олимпиады.
Хочется верить, что российская команда надерет всем пятую точку и завоюет
наибольшее количество золотых медалей. К тому моменту, когда ты будешь читать
эти строки, итоги олимпиады будут уже подведены. И в этом плане я тебе немного
завидую. Но сейчас, камрад, я скажу тебе вот что. Почему все эти люди стали
лучшими в мире спортсменами? В первую очередь потому, что у них есть большая
цель – олимпийское золото. Не просто цель – обогнать соседа Ваську или сбросить
пять килограмм лишнего веса. А настоящая большая цель. И поверь мне, в этом
половина успеха! Что самое удивительное, такой подход действует в любой сфере
приложения твоих усилий. Если у тебя есть БОЛЬШАЯ ЦЕЛЬ, успех неотвратим. Так
выпьем же за наши большие цели :).
INFO
Точки входа
Любой разрабатываемый тобой веб-фильтр для ISA Server должен иметь, как
минимум, одну точку входа. В качестве таковой всегда выступает пара определенных
функций. Для ISA Server 2004 это – функции GetWPXFilterVersion() и
HttpWPXFilterProc(). Для 2006-ой могут быть использованы как указанные выше
функции (сделано для того, чтобы сохранить совместимость с фильтрами,
написанными для предыдущей версии файрвола), так и оптимизированные под
возможности ISA Server 2006 функции GetFilterVersion() и HttpFilterProc().
Веб-фильтры
Веб-фильтры представляют собой особую разновидность расширений ISA Server.
Реагируя на различные события, возникающие в модуле Web proxy, веб-фильтры могут
анализировать HTTP-заголовки, принимая на основе такого анализа одно из
возможных решений. Все веб-фильтры реализованы в виде динамических библиотек,
инициализируемых в момент старта службы Microsoft Firewall и остающихся в памяти
до завершения работы службы.
Файрвол – жертва DoS-атак
При создании своего расширения для файрвола (не важно, ISA Server или любого
другого, поддерживающего написание расширений сторонними разработчиками), важно
помнить о том, что системная память не резиновая и устанавливать ограничения,
как по размеру обрабатываемых сетевых пакетов, так и по времени, отведенному на
обслуживание одной сессии. Иначе можно попасть в очень неприятную ситуацию,
когда файрвол, не сумев переварить слишком большую порцию данных, станет
причиной краха всей системы.
WWW
Залежи документации по ISA Server на MSDN -
msdn.microsoft.com/en-us/library/aa155227.aspx
Русскоязычное сообщество ISA Server –
www.isaserver.ru
Здесь можно скачать свежую версию как самого файрвола, так и SDK к нему -
go.microsoft.com/fwlink?linkid=41251
Кое-что о файрволах можно найти и в народной энциклопедии –
www.wikipedia.org/firewall
|