оглавление АДМИНИСТРИРОВАНИЕ
DeviceLock
Запуск Windows-приложений под Linux с помощью CrossOver Office Часть 2 Андрей Бешков tigrisha@sysadmins.ru
А ты что видишь? Удаленное управление посредством rdesktop, RAdmin, VNC Антон Борисов a.borisov@tesv.tmb.ru
Андрей Бешков tigrisha@sysadmins.ru
Три поросёнка Snort: 4 «Ниф-ниф», «Нуф-нуф» и «Наф-наф». (Настройка нескольких сенсоров Snort с помощью SnortCenter) Павел Закляков amdk7@mail.ru
12
Свободная ДОС для свободных людей, или Не Linux единым жив человек Андрей Маркелов andrew@markelov.net
20
42
Ошибки переполнения буфера извне и изнутри как обобщенный опыт реальных атак Крис Касперски kk@sendmail.ru
64
HARDWARE
С Юниксом на vi Сергей Супрунов amsand@rambler.ru
38
Реализация низкоуровневой поддержки 24 шины PCI в ядре операционной системы Linux Владимир Мешков ubob@mail.ru
БЕЗОПАСНОСТЬ
74
Свободный антивирус Обзор Clam AntiVirus – антивирусной программы для UNIXсистем. Сергей Яремчук grinder@ua.fm
32
ОБРАЗОВАНИЕ Программное управление ADSI: LDAP Иван Коробко ikorobko@prosv.ru
88
Уважаемые читатели! Рады сообщить вам, что с 1 апреля открыта подписка на II полугодие 2004 года. Подписной индекс 81655 по каталогу агентства «Роспечать». Подписной индекс 87836 по каталогам: Объединенный каталог «Пресса России» 2004/2 Каталог стран СНГ 2004/2 Каталог Казахстана 2004/2 Адресный каталог «Подписка за рабочим столом» 2004/2 Адресный каталог «Библиотечный каталог» 2004/2 ООО «Интер-почта» по тел. (095) 500-00-60 (Курьерская доставка по Москве).
Более подробная информация на нашем сайте www.samag.ru №3(16), март 2004
1
УНИКАЛЬНЫЕ СОБЫТИЯ НА КОМПЬЮТЕРНОМ РЫНКЕ Впервые организованная в 1989 году, выставка Неделя Информационных Технологий «IT Week Russia», известная в прошлом как Comtek, за пятнадцать лет своего существования пережила и взлеты и падения. В предыдущие годы в Неделю Информационных Технологий входила выставка Сomtek, которая состояла из нескольких направлений, и конференция E-Business. Развитие выставки в течение последних лет привело к необходимости выделить отдельные разделы в самостоятельные выставки. С этого года в Неделю Информационных Технологий будут входить пять самостоятельных выставок и две конференции. Этот шаг позволил расширить масштаб выставки, объединяющей все аспекты компьютерного бизнеса, что, в свою очередь, дает возможность привлечь к участию в выставке большее число компаний, занятых во всех сферах индустрии информационных технологий. Давид Патеишвили, директор выставки, сказал: «В этом году мы расширили темы, представленные на экспозиции, которые теперь охватывают все области компьютерной индустрии. Это дает превосходную возможность и участникам, и посетителям выставки принять участие и ознакомиться сразу с пятью выставками и двумя конференциями, проходящими в одно время и в одном месте. Это также позволит нам улучшить маркетинговую и рекламную кампании для каждой из выставок и конференций, проходящих в рамках Недели Информационных Технологий, с учетом целевой аудитории, специфичной для каждой из них. Хочется добавить, что некоторые выставки, которые будут проходить в рамках Недели Информационных технологий, не имеют аналогов в России, являясь тем самым уникальными». В рамках Недели Информационных Технологий пройдут следующие выставки и конференции: 1. Personal Computing Expo – общая, неспециализированная выставка, ориентированная на конечных пользователей. В ней представлены производители и дистрибьюторы персональных компьютеров и периферии, компьютерных игр, дистрибьюторы сотовой техники и портативных компьютеров, интернет- и контентпровайдеры и многие другие. 2. Hardware & Peripherals Expo – специализированная выставка, на которой представлены: компьютеры, мониторы, периферийные устройства, комплектующие, накопители, коммуникационное оборудование и услуги, т.е. весь спектр hardware, ориентированного на ведение бизнеса. 3. Международная конференция eLearning Russia (Информационные технологии в образовании), на которой будут освещены последние достижения образовательных технологий в школах, вузах, а также рассмотрены вопросы дистанционного и бизнес-образования.
2
4. Software Expo – специализированная выставка, ориентированная на программные продукты для систем бухгалтерского и складского учета, комплексного ПО управления предприятием, систем управления документооборотом, систем распознавания документов, разработку ПО, защиту информации. В рамках этой выставки будет подготовлен цикл тематических семинаров, посвященных актуальным вопросам в области разработки экономических программ и систем управления бизнесом. 5. Специализированная выставка CAD/CAM/CAE представляет системы автоматизированного проектирования. Для большинства российских производителей необходимость использования САПР для оптимизации работы предприятия стала очевидной. Особенно ярко это проявляется в таких отраслях, как авиастроение, автомобилестроение, тяжелое машиностроение, архитектура, строительство, нефтегазовая промышленность. 6. eLearn Expo – специализированная выставка, на которой будут демонстрироваться новейшие продукты и технологии в сфере электронного обучения, предназначенные для коллективного и индивидуального пользования. Дистанционное обучение через сети Internet и Intranet, получившее широкое распространение в развитых странах, становится все более актуальным и для России. 7. eBusiness Russia (Электронный бизнес в России) – международная конференция, посвященная вопросам автоматизации бизнес-процессов, развития электронной коммерции, подбора ИТ-персонала. Выставка IT-week остается ведущей международной выставкой информационных технологий в России и странах СНГ. Это уникальное место для проведения переговоров с первыми лицами сразу нескольких крупных фирмпоставщиков оборудования и решений. Практически все крупные западные вендоры присутствуют на выставке непосредственно или при посредстве своих российских партнеров. В соответствии с растущими потребностями рынка и увеличением числа участников экспозиция расширила площадь, которая в этом году составит 8 000 кв. м. В выставках, которые пройдут в течение 4 дней, примут участие 250 ведущих компаний отрасли из 25 стран мира. Ожидается, что число посетителей выставки превысит 75 000 человек, включая руководителей верхнего и среднего звена, технических специалистов и IT-администраторов, из более 500 городов России и СНГ. «IT Week 2004», 15-ая Международная Выставка Информационных Технологий пройдет в «Экспоцентре» на Красной Пресне в Москве с 26 по 29 апреля 2004 года.
администрирование
ЗАПУСК WINDOWS-ПРИЛОЖЕНИЙ ПОД LINUX C ПОМОЩЬЮ CROSSOVER OFFICE ЧАСТЬ 2
В первой статье о CrossOver Office мы обсудили, как выполнить инсталляцию этого программного пакета. Затем поговорили о том, как с помощью него запускать под управлением Linux программы, написанные для Windows. В качестве примера было рассказано об успешной работе с Microsoft Office 2000, The Bat!, Microsoft Internet Explorer, Mplayer и Outlook Express. Тем, кто пропустил первую статью, рекомендую обязательно ознакомиться с ней. Сделать это можно либо в февральском выпуске этого журнала, либо на моем сайте http://onix.opennet.ru. Ну а я потихоньку продолжу линию нашего повествования.
АНДРЕЙ БЕШКОВ 4
администрирование Задача на сегодня довольно проста. Нужно изучить теорию функционирования и глубинные механизмы CrossOver Office. В дальнейшем это поможет нам правильно идентифицировать проблемы, возникающие при инсталляции Windows-программ, а, как говорят врачи, правильный диагноз – половина лечения. Как было отмечено в первой статье, все Windowsпрограммы делятся на два вида. Официально поддерживаемое обеспечение скорее всего легко установится и будет гладко работать под управлением CrossOver Office, навевая на вас мысли о невыносимой легкости бытия Linux-пользователя. К сожалению, список этих программ не так уж велик. Ознакомиться с ним можно по следующему адресу: http://www.codeweavers.com/site/compatibility/browse/cat. Кстати, стоит отметить, что в него входят только те приложения, за стабильную работу которых сотрудники компании Codeveawers могут поручиться на все сто процентов. К сожалению, сотрудников в компании не так уж и много, поэтому они не могут знать о всех используемых вами Windows-приложениях. Впрочем, даже если они и будут знать, это не очень изменит положение вещей. Ведь на тестирование каждого приложения нужно потратить немало времени. Таким образом, получается, что приложения, не подвергшиеся тестированию под CrossOver Office, не получают официальной поддержки. Ничего страшного в этом нет. Возможно, при работе с нашими Windows-программами все будет хорошо, и нам никогда не придется прибегать к поддержке персонала Сodeveawers. Например, я довольно легко установил Remote Administrator, QuickTime Player, Acrobat Reader и Microsoft Visio и весьма успешно работаю с этими приложениями. Все вышесказанное означает только то, что, начав работать с неподдерживаемыми программами, надеяться нам не на кого и мы, как кошки, начинаем гулять сами по себе. Впрочем, это ни в коем случае не должно страшить нас. В связи с тем, что CrossOver Office основан на коде, унаследованном от Wine, практически все, о чем я буду рассказывать в этой статье, может вполне успешно применяться при работе с Wine. Итак, начнем с самых основ и посмотрим, что происходит в системе во время запуска Windows-приложений. Сам по себе процесс загрузки программы в память несложен. Проблема в том, что нужно найти и загрузить все требуемые DLL. Затем провести импортирование из DLL требуемых функций и правильно найти точки входа в каждую из них. Предполагается, что каждое нормально написанное приложение, как и любые небазовые DLL, не станет пытаться использовать напрямую системные функции, а вместо этого будет импортировать все требуемые механизмы из основных системных библиотек. Соответственно для того, чтобы Wine смогла нормально загружать Windows-приложения, нужно как минимум заменить своими собственными реализациями основные системные DLL, в число которых входят USER/USER32, GDI/GDI32, KERNEL/KERNEL32 и NTDLL. Кстати, стоит отметить, что многие функции, реализованные изначально в KERNEL32 и ADVAPI32, постепенно были перенесены в NTDLL. Динамические библиотеки, заново реализованные для Wine, называются «встроенными», а те, что перенесены из Windows без каких-либо изменений, называются «родными». Соответственно в тот момент, когда Windows-приложение заявляет о своем желании импорти-
№3(16), март 2004
ровать какую-либо DLL, Wine сначала смотрит в список встроенных DLL, если там найти нужное не удалось, то начинается поиск на жестком диске родного Windows DLL-файла. Сердцем системы эмуляции служит Wine-сервер, если говорить по существу, то он необходим для правильной организации межпроцессорных коммуникаций между всеми запущенными Windows-приложениями и является отдельным однопоточным процессом с бесконечным циклом выборки и обработки сообщений, приходящих от системы и клиентских процессов. Из-за отсутствия многопоточности внутри своего процесса Wine-сервер не может выполнять задачи, требующие существенных временных затрат. Впрочем передача сообщений и событий между клиентскими процессами и основной системой действительно не является трудоемкой работой. Также в целях безопасности Wine-сервер не имеет доступа к адресному пространству своих клиентов. Если в момент старта первого клиентского Wine-процесса процесс Wine-сервера еще не работает, то он будет незамедлительно запущен. Сразу же после начала работы Wine-сервер создает UNIX-сокет, предназначенный для общения с клиентами, отвечающими за выполнение Windowsприложений. Обычно сокет находится в директории $HOME/ .wine либо там, куда указывает переменная окружения WINEPREFIX. Ну а если мы используем CrossOver Office, то сокет возникнет в директории /tmp/wine-<имя пользователя>. После того как все клиентские Wine-процессы будут завершены, закончит свою работу и Wine-сервер. Методы работы с приложениями win32 и win16 довольно сильно отличаются. Для программ с архитектурой win16, доставшихся нам в наследство от старых версий Windows, характерно функционирование в едином адресном пространстве и использование кооперативной многозадачности. Исходя из таких требований, удобнее всего было сделать так, чтобы все запущенные win16-программы выполнялись не как отдельные процессы, а стали нитями в рамках одного процесса. Идея подобного механизма работы с устаревшими приложениями была позаимствована из Windows NT, поэтому так же, как и в системе-прародителе, сущность, обеспечивающая функционирование приложений win16, называется WOWпроцессом. Внутри него разные нити, олицетворяющие отдельные приложения, синхронизируются друг с другом с помощью мьютекса. Нить, работающая в данный момент, захватывает мьютекс в эксклюзивное владение и соответственно не позволяет работать остальным нитям. Как только активная нить посчитает, что выполнила достаточно действий, мьютекс будет освобожден и начнет подавать сигналы, указывающие на его холостятский статус. В результате этого он будет вновь захвачен следующей нитью, стоящей в очереди на выполнение. Таким образом реализуется кооперативная многозадачность. Ну а WOW-процесс будет существовать до тех пор, пока внутри него работает хотя бы одна нить. Приемы, используемые для работы с win32-программами, выглядят совершенно иначе. Для каждого выполняемого внутри Wine приложения win32 создается свой собственный процесс, так как программы такого типа написаны в расчете на вытесняющую многозадачность и отдельное виртуальное адресное пространство для каждого экземпляра. Каждый клиентский процесс Wine имеет в своем составе специальную сервисную нить, выполняющуюся наравне с остальны-
5
администрирование ми нитями этого процесса. Ее функция состоит в том, чтобы выполнять задачи, которые нельзя поручить Wine-серверу. Например, с помощью него удается реализовать для каждого процесса отдельную очередь ожидания событий, исходящих от системы и других Wine-процессов. По приходу события сервисная нить процесса выходит из состояния сна, обрабатывает поступившее событие и передает сообщение о нем другим нитям процесса. Любые компоненты ядра wine могут устанавливать свои собственные обработчики в сервисную нить, если есть необходимость выполнять какие-либо действия вне зависимости от цикла выборки сообщений эмулируемого приложения. Следующей проблемой является поддержка реестра. Ведь в любой, даже свежеустановленной Windows-системе в реестре уже записано довольно большое количество данных, поэтому нам необходимо иметь свою собственную реализацию реестра. В отличие от Windows, где реестр хранится в бинарном формате, wine использует для этой цели обычный текстовый файл. Соответственно ключи реестра записываются примерно в таком формате: [AINF0008\\0.map] 1069188705 "008cc782b199a527"=",33,HKCR,Interface\\ ↵ {56a868b5-0ad4-11ce-b03a-0020af0ba770}\\Distributor,,"i "140e1cba790e4932"=",33,HKLM,Software\\Microsoft\\ ↵ Multimedia\\DirectXMedia,.Prog,"
А это значит, что в случае необходимости мы легко сможем добавлять или удалять данные в этот импровизированный реестр с помощью обычного текстового редактора. Текстовый формат хранения данных не единственное отличие реестра, используемого Wine, от настоящего Windowsреестра. Дело в том, что под Wine реестр хранится не в одном, а сразу в нескольких отдельных файлах. Сразу же после инсталляции CrossOver Office ветви реестра, обеспечивающие базовую функциональность Windows, будут записаны в файл system.reg, находящийся в директории $HOME/.cxoffice/dotwine. Дабы не испортить системный реестр, все изменения, вносимые в него во время установки и последующего жизненного цикла пользовательских программ, записываются в отдельный файл user.reg. Следующим условием, необходимым для правильного функционирования Windows-программ, является наличие на диске строго определенной иерархии директорий. С точки зрения Windows наши директории должны выглядеть так.
CrossOver Office хранит все эти директории в $HOME/ .cxoffice/dotwine/fake_windows/. Создаются они автоматически и так же легко наполняются всеми необходимыми файлами сразу же после запуска программы /opt/cxoffice/ bin/officesetup. Вот теперь, когда мы уже разобрались со многими нюансами работы эмулятора, пришло время посмотреть, как выглядит изнутри главный конфигурационный файл. Для Wine этот файл называется $HOME/.wine/wine.conf, а для CrossOver соответственно $HOME/.cxoffice/dotwine/config. Формат обоих файлов почти ничем не отличается в обеих реализациях. Первые несколько секций отвечают за определение виртуальных дисков для Windows-системы. Проблема состоит в том, что в UNIX-подобных операционных системах, в отличие от Windows, файловая система не разбита на отдельные диски, обозначаемые латинскими буквами от A до Z, а представлена единым деревом. Ну а вторая досадная неувязка кроется в том, что традиционная файловая система Windows не различает строчные и прописные буквы в именах файлов и директорий. Соответственно файл с именем MyDOCUMENT и mydocument для Windows являются одним и тем же. Таким образом, перед нами стоит задача сделать так, чтобы Windows считала некоторые директории файловой системы UNIX своими виртуальными дисками. Кстати, стоит отметить, что те строки, которые начинаются знаком «;», воспринимаются эмулятором как комментарии. # Èìÿ âèðòóàëüíîãî äèñêà.  äàííîì ñëó÷àå ýòî äèñê A: [Drive A] # Ïóòü ê äèðåêòîðèè, êîòîðàÿ õðàíèò â ñåáå ôàéëû. Ïî óìîë÷àíèþ # ñêðèïò èíñòàëëÿöèè âïèñûâàåò ñþäà äèðåêòîðèþ /mnt/floppy/, # íî äëÿ ìåíÿ ýòî íå ïîäõîäèò â ñâÿçè ñ òåì, ÷òî â ìîåé # ñèñòåìå ðàáîòàåò äåìîí autofs, êîòîðûé ìîíòèðóåò óñòðîéñòâî # äèñêîâîäà /dev/floppy â äèðåêòîðèþ /mnt/floppy/auto/. # Ïîýòîìó ïðèøëîñü ïîïðàâèòü ïóòü âðó÷íóþ. "Path" = "/mnt/floppy/auto" # Òèï óñòðîéñòâà. Äóìàþ, âñåì ïîíÿòíî, ÷òî äëÿ Windows îíî # áóäåò âûãëÿäåòü êàê ãèáêèé äèñê. Ýòà ïåðåìåííàÿ ìîæåò # ïðèíèìàòü çíà÷åíèÿ hd, cdrom, network, floppy ñîîòâåòñòâåííî # îáîçíà÷àþùèå æåñòêèé äèñê, CD-ROM, ñåòåâàÿ ïàïêà, # ãèáêèé äèñê. "Type" = "floppy" # Ìåòêà òîìà. Îáû÷íî íóæíà òîëüêî äëÿ ïðîãðàìì, êîòîðûå # ïûòàþòñÿ çàùèùàòüñÿ îò êîïèðîâàíèÿ ñ ïîìîùüþ ÷òåíèÿ ýòîé # ìåòêè. "Label" = "Floppy A" # Èìÿ ôèçè÷åñêîãî óñòðîéñòâà îáû÷íî èñïîëüçóåòñÿ äëÿ # íèçêîóðîâíåâîãî ÷òåíèÿ è çàïèñè. Ýòó îïöèþ ìîæíî ïðèìåíÿòü # òîëüêî ê ãèáêèì äèñêàì è CD-ROM. # Åñëè ïîïûòàòüñÿ ïðèìåíèòü òàêóþ îïöèþ ê ëþáîìó äðóãîìó # óñòðîéñòâó, òî ðåçóëüòàòû áóäóò î÷åíü íåïðèÿòíûå. # Íèçêîóðîâíåâàÿ çàïèñü ñêîðåå âñåãî ïîâðåäèò ðîäíóþ UNIX # ôàéëîâóþ ñèñòåìó. "Device" = "auto" # Îïèñàíèå äèñêà Ñ: [Drive C] "Path" = "fake_windows" # Ñóäÿ ïî òèïó óñòðîéñòâà, Windows áóäåò ñ÷èòàòü, ÷òî ýòî # æåñòêèé äèñê. "Type" = "hd" "Label" = "fake_windows" # Òèï ôàéëîâîé ñèñòåìû, ïîâåäåíèþ êîòîðîé Wine áóäåò # ïîäðàæàòü. Ìîæåò ïðèíèìàòü çíà÷åíèÿ: win95, msdos, unix. # Ñîîòâåòñòâåííî â ñëó÷àå îïöèè win95 ýìóëèðóåìàÿ ñèñòåìà áóäåò # äóìàòü, ÷òî ýòî FAT32 ñ ïîääåðæêîé äëèííûõ èìåí. Ðåãèñòð # ñèìâîëîâ â èìåíàõ ôàéëîâ íå ðàçëè÷àåòñÿ. Åñëè èñïîëüçîâàòü # òèï msdos, òî íà èìåíà ôàéëîâ íàêëàäûâàåòñÿ îãðàíè÷åíèå, # ñîîòâåòñòâóþùåå ôàéëîâîé ñèñòåìå MS-DOS. Îïöèÿ unix # îòîáðàæàåò ôàéëîâóþ ñèñòåìó òî÷íî òàê æå, êàê îíà âûãëÿäèò # äëÿ UNIX. Ýòà îïöèÿ ïðàêòè÷åñêè íå ïðèìåíÿòñÿ, òàê êàê # Windows íå óìååò ðàáîòàòü ñ òàêèìè ôàéëîâûìè ñèñòåìàìè. "Filesystem" = "win95"
6
администрирование # Îïöèÿ, îòâå÷àþùàÿ çà ïåðåêîäèðîâàíèå èìåí ôàéëîâ # è äèðåêòîðèé â ðîäíóþ êîäèðîâêó. "Codepage" = "0" # Îïèñàíèå äèñêà M: [Drive M] # Ýòî ïåðâûé CD-ROM â ñèñòåìå. Êàê îáû÷íî, èç-çà äåìîíà # autofs ïðèøëîñü ïîìåíÿòü ïóòü ñ /mnt/cdrom íà /mnt/cdrom/auto "Path" = "/mnt/cdrom/auto" "Type" = "cdrom" "Label" = "CD-ROM M" "Filesystem" = "win95" "Device" = "auto" # Îïèñàíèå äèñêà N: [Drive N] # Âòîðîé CD-ROM. Âñå óñòàíîâêè ñäåëàíû àíàëîãè÷íî äèñêó M. "Path" = "/mnt/cdrom1/auto" "Type" = "cdrom" "Label" = "CD-ROM N" "Filesystem" = "win95" "Device" = "auto" # Îïèñàíèå äèñêà Y: [Drive Y] # Çäåñü â êà÷åñòâå äèñêà Y ìîíòèðóåòñÿ äîìàøíÿÿ äèðåêòîðèÿ # ïîëüçîâàòåëÿ. "Path" = "%HOME%" # À âîò òàêîé òèï óñòðîéñòâ â ñòàíäàðòíîì Wine íå âñòðå÷àåòñÿ, # îí õàðàêòåðåí òîëüêî äëÿ CrossOver Office. Âïðî÷åì, äëÿ Windows # ýòî óñòðîéñòâî âñå ðàâíî âûãëÿäèò, êàê îáû÷íûé æåñòêèé äèñê. "Type" = "%CXOFFICE_DRIVE_TYPE_HACK%" "Label" = "Home" "Filesystem" = "win95" "Codepage" = "0" # Îïèñàíèå äèñêà Z: [Drive Z] # À òóò ìîíòèðóåòñÿ êîðåíü âñåé ôàéëîâîé ñèñòåìû. "Path" = "/" "Type" = "%CXOFFICE_DRIVE_TYPE_HACK%" "Label" = "Root" "Filesystem" = "win95" "Codepage" = "0"
Разобравшись с виртуальными дисками, идем дальше. Следующая интересная для нас секция выглядит так: [wine] # Èìåíà ñèñòåìíûõ äèðåêòîðèé "Windows" = "c:\\Windows" "System" = "c:\\Windows\\system" "Temp" = "c:\\Windows\\Temp" # Ñèñòåìíàÿ ïåðåìåííàÿ PATH óêàçûâàåò, ãäå è â êàêîì ïîðÿäêå # çàãðóæàåìûå ïðîãðàììû äîëæíû èñêàòü íåîáõîäèìûå äëÿ ðàáîòû # áèáëèîòåêè è óòèëèòû. "Path" = "c:\\Windows;c:\\Windows\\system;y:\\" # Äàííàÿ îïöèÿ óêàçûâàåò, ãäå äîëæíû õðàíèòüñÿ ïîëüçîâàòåëüñêèå # ïðîôèëè, íî â ñâÿçè ñ òåì, ÷òî ó íàñ âñåãî îäèí ïîëüçîâàòåëü, # îíà íèêîãäà íå èñïîëüçóåòñÿ è ïîýòîìó çàêîììåíòèðîâàíà. ;;"Profile" = "c:\\Windows\\Profiles\\Administrator" # Êàêóþ ãðàôè÷åñêóþ ïîäñèñòåìó íóæíî èñïîëüçîâàòü äëÿ ðèñîâàíèÿ # ýêðàííûõ îáúåêòîâ. "GraphicsDriver" = "x11drv" # Äîëæíû ëè Windows-ïðîãðàììû âèäåòü ôàéëû, ó êîòîðûõ ïåðâûì # ñèìâîëîì èìåíè ÿâëÿåòñÿ òî÷êà. Îáû÷íî òàêèå ôàéëû ñ÷èòàþòñÿ # ñêðûòûìè. ;;"ShowDotFiles" = "1" # Ìîæíî ëè ïîêàçûâàòü ñèìâîëè÷åñêèå ññûëêè íà äèðåêòîðèè? # Îáû÷íî ýòî ïðèâîäèò ê çàâèñàíèþ Windows-ïðîãðàìì, # ïûòàþùèõñÿ äåëàòü ðåêóðñèâíûé îáõîä äèðåêòîðèé. ×àùå âñåãî # â ýòó ëîâóøêó ïîïàäàþòñÿ ðàçíîîáðàçíûå àâòîìàòè÷åñêèå # èíñòàëëÿòîðû. ;;"ShowDirSymlinks" = "1" # Óêàçàíèå íà ñêðèïò, îòâå÷àþùèé çà ñîçäàíèå è óñòàíîâêó # ïðàâèëüíûõ èêîíîê äëÿ Windows-ïðîãðàìì âíóòðè âàøåãî îêîííîãî # ìåíåäæåðà.  êà÷åñòâå ìåíåäæåðîâ, ê ïðèìåðó, ìîãóò âûñòóïàòü # GNOME, KDE èëè CDE. "ShellLinker" = "wineshelllink" # Ïðîãðàììà, îòâå÷àþùàÿ çà ñîçäàíèå è îáñëóæèâàíèå ìåíþ
№3(16), март 2004
# ïðîãðàìì îêîííîãî ìåíåäæåðà "LinkProcessor" = "winemenubuilder.exe" # Òóò íàõîäÿòñÿ èêîíêè. Êñòàòè, ñòîèò îòìåòèòü, ÷òî # äëÿ óäîáñòâà îáðàùåíèÿ îíè àâòîìàòè÷åñêè êîíâåðòèðóþòñÿ # èç ôîðìàòà ico â xpm. "IconsDir" = "c:\\Windows\\Icons" # Íó à çäåñü ëåæèò áèáëèîòåêà, îòâå÷àþùàÿ çà ðàáîòó # ñ FreeType-øðèôòàìè. "FreeTypeLib" = "libcxfreetype.so" [Restart] # Ïðîãðàììà ïî èäåå äîëæíà îòâå÷àòü çà ïåðåçàãðóçêó Windows. # Èíòåðåñíî, ÷òî òàêîãî ôàéëà íà äèñêå íå ñóùåñòâóåò. # À åãî ôóíêöèè âûïîëíÿåò ñêðèïò /opt/cxoffice/bin/cxreboot. # Âèäèìî, êòî-òî èç ðàçðàáîò÷èêîâ äîïóñòèë òóò îøèáêó. "Boot" = "c:\\Windows\\System\\reboot.exe" # [wineconf] [Version] # Êàêóþ âåðñèþ Windows äîëæåí èìèòðèðîâàòü ýìóëÿòîð? # Îïöèÿ ìîæåò ïðèíèìàòü çíà÷åíèÿ: win95, win98, winme, nt351, # nt40, win2k, winxp, win2k3, win20, win30, win31. Íåêîòîðûå # ïðîãðàììû áóäóò ðàáîòàòü òîëüêî ïîä îïðåäåëåííîé âåðñèåé. "Windows" = "win98" # Âåðñèÿ MS-DOS. Íóæíà äëÿ íåêîòîðûõ ñòàðûõ ïðîãðàìì. ;;"DOS" = "6.22"
Следующая интересная для нас секция конфигурационного файла управляет порядком загрузки динамических библиотек. Иногда случается так, что Windows-приложение лучше работает с родными библиотеками, чем с теми, что предлагает эмулятор. [DllOverrides] "ole2" = "native, builtin" "ole2nls" = "native, builtin" "*comctl32" = "builtin" "*ICWCONN1.EXE" = "builtin" "*IEINFO5.OCX" = "builtin"
Процедура загрузки библиотек читает опции слева направо. Соответственно, к примеру, для ole32 ключевое слово native означает, что эмулятор должен сначала найти настоящую Windows-библиотеку и только в случае неудачи пытаться подменить ее встроенной версией. Как видите, описание имен библиотек позволяет использовать шаблоны. Конечно, иногда изменение порядка загрузки тех или иных компонентов помогает оживить те или иные безнадежные Windows-приложения. Но все же стоит отдавать себе отчет, что жонглирование библиотеками может запросто довести систему эмуляции до критических ошибок. Впрочем, особенно пугаться не стоит, как только порядок загрузки библиотек будет восстановлен, все сразу же вернется на круги своя. Разобравшись с понятием подмены загружаемых библиотек, идем дальше по файлу конфигурации к секции, отвечающей за работу с графическими драйверами. Драйвер X11, используемый в Wine для работы с графикой, по замыслу разработчиков должен заниматься отрисовкой элементов графического интерфейса только внутри окон, предоставляемых системным оконным менеджером, и не более того. А менеджер, в свою очередь, не вторгается на территорию окон, предоставленных Wine, и всего лишь управляет их взаимоотношениями с основной системой. [x11drv] # Êîëè÷åñòâî öâåòîâ, âûäåëÿåìûõ èç ñèñòåìíîé ïàëèòðû äëÿ íóæä # Windows-ïðîãðàìì. Äåéñòâóåò òîëüêî â òîì ñëó÷àå, êîãäà
7
администрирование # ãðàôè÷åñêàÿ ïîäñèñòåìà ðàáîòàåò â ðåæèìå ãëóáèíû öâåòà # 8 áèò íà îäèí ïèêñåëü èëè 256 öâåòîâ. "AllocSystemColors" = "100" "PrivateColorMap" = "N" # Ïûòàòüñÿ ëè èñïîëüçîâàòü áîëåå ìåäëåííûå îïåðàöèè âìåñòî # áûñòðûõ äëÿ óëó÷øåíèÿ êà÷åñòâà îòðèñîâêè. Ëè÷íî ìíå # ïîêàçàëîñü, ÷òî â áîëüøèíñòâå ñëó÷àåâ ðàçíèöà íå áóäåò çàìåòíà. "PerfectGraphics" = "N" # Óêàçûâàåò, êàêóþ ãëóáèíó öâåòà íåîáõîäèìî èñïîëüçîâàòü. # Èìååò ñìûñë òîëüêî äëÿ äèñïëååâ, óìåþùèõ ðàáîòàòü â ðåæèìå # multi-depth. Ïî óìîë÷àíèþ îòêëþ÷åíî, òàê êàê èñïîëüçóåòñÿ # êðàéíå ðåäêî. ;;"ScreenDepth" = "16" # Èìÿ äèñïëåÿ, íà êîòîðûé íóæíî âûâîäèòü ãðàôèêó. ;;"Display" = ":0.0" # Äàííàÿ îïöèÿ ïîêàçûâàåò, ðàçðåøåíî ëè îêîííîìó ìåíåäæåðó # óïðàâëÿòü îêíàìè Wine "Managed" = "Y" # # # # # ;
 ñëó÷àå åñëè ïðåäûäóùàÿ îïöèÿ àêòèâèðîâàíà, ìîæíî ëè ïîçâîëèòü ìåíåäæåðó ðèñîâàòü ðàìêè îêîí è ïðî÷èå ýëåìåíòû. Ýòè äâå îïöèè óëó÷øàþò èíòåãðàöèþ ýìóëèðóåìîãî ïðèëîæåíèÿ ñ îñòàëüíûìè ïðîãðàììàìè, íî íå âñå çàäà÷è ìîãóò íîðìàëüíî ðàáîòàòü â òàêîì ðåæèìå. WMFrames = "Y"
# Äàííûé ðåæèì âûñòóïàåò êîíêóðåíòîì äëÿ äâóõ ïðåäûäóùèõ îïöèé # è ïîçâîëÿåò ðàáîòàòü Wine òàê, ñëîâíî ýòî íå ïðîñòî îêíî, # à âèðòóàëüíûé äåñêòîï. Ñîîòâåòñòâåííî âñå Windows-ïðèëîæåíèÿ # áóäóò ðèñîâàòü ñâîè îêíà òîëüêî âíóòðè ýòîãî äåñêòîïà, òàê êàê # èì ýòî áîëüøå âñåãî ïîäõîäèò. Çà óïðàâëåíèå îêíàìè âíóòðè # âèðòóàëüíîãî äåñêòîïà îòâå÷àåò Wine. Òàêîé ðåæèì ïîçâîëÿåò # èçáåæàòü âçàèìîäåéñòâèÿ Windows-ïðèëîæåíèé ñ ñèñòåìíûì # îêîííûì ìåíåäæåðîì, ïîýòîìó îí íàèáîëåå ñîâìåñòèì c ìîäåëüþ # ðèñîâàíèÿ, èñïîëüçóåìîé íàñòîÿùåé Windows. ;;"Desktop" = "640x480" # Íóæíî ëè, ÷òîáû ïðèëîæåíèÿ, ðàáîòàþùèå ñ DirectDraw, ìîãëè # âîñïîëüçîâàòüñÿ ïðåèìóùåñòâàìè ðàáîòû ñ ãðàôè÷åñêîé ïîäñèñòåìîé # â ðåæèìå DGA (XFree86 Direct Graphic Architecture). Îáÿçàòåëüíî # óáåäèòåñü, ÷òî ó âàñ åñòü äîñòóï ê óñòðîéñòâó /dev/mem "UseDGA" = "Y" # Åñëè ñ DGA ïîðàáîòàòü íå ïîëó÷èëîñü, òî ìîæíî ïîïðîáîâàòü # çàñòàâèòü DirectX èñïîëüçîâàòü ðàçäåëÿåìóþ ïàìÿòü äëÿ îáìåíà # äàííûìè ñ âèäåîïîäñèñòåìîé. Êîíå÷íî, ýòî áóäåò ðàáîòàòü # ìåäëåííåå, ÷åì DGA, íî âñå æå áûñòðåå, ÷åì ïðèìåíÿåìûé # ñòàíäàðòíî îáìåí äàííûìè ñ X11 ÷åðåç ñîêåò. "UseXShm" = "Y" # Ðàçðåøåíèå èñïîëüçîâàòü äëÿ îòðèñîâêè ðåæèì XVideo "UseXVidMode" = "Y" # Óëó÷øåííîå óïðàâëåíèå îêíîì, ïîëó÷èâøèì ôîêóñ ââîäà. "UseTakeFocus" = "Y"
[serialports] "Com1" = "/dev/ttyS0" "Com2" = "/dev/ttyS1" "Com3" = "/dev/ttyS2" "Com4" = "/dev/modem"
Вот тут у нас расписаны опции, необходимые для нормальной работы LPT-порта и принтера, если он, конечно, к нему присоединен. Ну и конечно же, описан стандартный механизм работы с файлом подкачки и фильтрами принтера. [parallelports] "Lpt1" = "/dev/lp0" [spooler] "FILE:" = "LPT1:" = "LPT2:" = "LPT3:" =
"tmp.ps" "|lpr" "|gs -sDEVICE=bj200 -sOutputFile=/tmp/fred -q -" "/dev/lp3
Кстати, стоит отметить, что работа с принтером в CrossOver Office сделана очень удобно. Несмотря на то что в моей системе для печати используется CUPS, приложения Windows автоматически опознали все доступные системе принтеры и без каких-либо дополнительных настроек стали отлично с ними работать. Следующая сессия указывает, какой стиль иконок, кнопок и прочего оформления использовать для рисования внутри эмулируемых приложений. Доступны стили Win31, Win95 и Win98. [Tweak.Layout] "WineLook" = "Win95"
Далее в конфигурационном файле идут описания настроек звуковой системы, но на них мы тоже не станем останавливаться, потому что они просты и понятны. Итак, закончив с изучением внутреннего строения CrossOver Office, перейдем к решению насущных проблем. В первой статье мы столкнулись с одним досадным неудобством. При пользовании программой /opt/cxoffice/bin/ officesetup было замечено, что на вкладке «Menus» есть список меню, созданных установленными приложениями и выглядящий вот так.
# Îïöèÿ äëÿ âêëþ÷åíèÿ ïðîäâèíóòîãî ñïîñîáà êîíòðîëÿ çà ìûøüþ. # Î÷åíü ïîëåçíà äëÿ ïðèëîæåíèé, èñïîëüçóþùèõ DirectX "DXGrab" = "N" # Âêëþ÷àåò äâîéíóþ áóôåðèçàöèþ îêíà, â êîòîðîì îòðèñîâûâàåòñÿ # äåñêòîï. Ïðèìåíÿåòñÿ òîëüêî âìåñòå ñ îïöèåé Desktop, îïèñàííîé # âûøå. Î÷åíü ïîìîãàåò ïðè ðàáîòå ñ ïðèëîæåíèÿìè, èíòåíñèâíî # èñïîëüçóþùèìè OpenGL . "DesktopDoubleBuffered" = "N" # Íîìåð ïîðòà äëÿ ðåæèìà XVideo. Íóæíî èñïîëüçîâàòü òîëüêî â # ñëó÷àå, åñëè ó íàñ íåñêîëüêî ïîðòîâ. ;; "XVideoPort" = "43" # Îïöèÿ, óêàçûâàþùàÿ, íóæíî ëè ðàáîòàòü â ñèíõðîííîì ðåæèìå. # Îáû÷íî èñïîëüçóåòñÿ äëÿ îòëàäêè ïðîáëåì ñ X11. ;;"Synchronous" = "Y"
Дальше в этой секции идут настройки шрифтов, используемые для работы Windows-программ. Значения всех параметров по умолчанию срабатывают нормально, поэтому заострять внимание на них мы не будем. При наличии интереса все желающие могут ознакомиться с ними самостоятельно. Далее идет описание привязки виртуальных портов к физическим устройствам.
8
Соответственно с помощью нажатия на кнопку «Recreate menu entry» мы могли бы автоматически создавать пункты меню для отображения в оконном менеджере. Но, к сожалению, с русскими названиями меню CrossOver Office дружить не захотел и при каждой попытке создать то или иное меню выводил вот такую ошибку.
администрирование не проблема, но не тут-то было. Тем, кто использует для выхода в Интернет прокси-сервер, не повезло, программа инсталляции скачает только первый кусочек обновления и затем выдаст ошибку, сообщающую о невозможности найти сайт, на котором находятся оcтальные файлы, необходимые для успешного завершения начатого дела.
Для исправления данной проблемы, мешающей нам создавать меню, нужно открыть файл $HOME/.gnome2/ vfolders/applications.vfolder-info, найти в нем все словосочетания, содержащие следующий набор букв «яПЕДЯРБЮ» и удалить эти странные символы. Такое непонятное слово получается, если неправильно перевести слово «средства», содержащееся в названии меню «Средства Microsoft Office» из кодировки cp1251 в koi8-r. После того как мы выкорчевали все встречные слова «яПЕДЯРБЮ», создание пунктов меню для оконного менеджера должно заработать так, как и положено. После этого конвертируем файл-заготовку в нужную кодировку и обновляем системное меню. $ iconv -f cp-1251 -t utf-8 $HOME/.menu/cxoffice > ↵ $HOME/.menu/cxoffice $ update-menus –n –u
Подробно о смысле этих действий я писал в предыдущей статье. На следующей картинке в качестве примера вы можете посмотреть, как выглядит мое меню Windows-приложений.
Поэтому нам нужно будет скачать шестую версию вручную и установить ее самостоятельно. Делается это довольно просто. Для начала убедимся, что версия Windows, под которую маскируется CrossOver, называется win98. Для тех, кто забыл, как это сделать, напоминаю, что нужно посмотреть в файл config и найти в нем секцию [Version]. На отдельной машине или используя VMWare, запускаем операционную систему Windows 98. Данная машина может быть подключена к Интернету через прокси-сервер или напрямую. Скачиваем первую часть инсталлятора, соответствующую языку используемого у вас браузера: http://www.microsoft.com/ windows/ie/downloads/critical/ie6sp1/default.asp. После запуска инсталляции используем опцию выборочной установки компонентов.
В дереве компонентов я выбрал следующие пункты:
Кстати, это всего лишь малый список Windows-приложений, используемых мною повседневно. Разобравшись с этой проблемой, давайте двинемся дальше. Беда в том, что многие современные приложения для правильного функционирования очень сильно опираются на компоненты, предоставляемые Microsoft Internet Explorer. И все было бы здорово, если бы не вечная гонка за новизной. После инсталляции Microsoft Office 2000 или Microsoft Office XP в системе уже присутствует Microsoft Internet Explorer версии 5.0, но многим устанавливаемым программам этого, к сожалению, недостаточно, им на блюдечке подай шестую версию. Казалось бы, проблема не стоит выеденного яйца, обновить версию вышеуказанного продукта с помощью CrossOver Office
№3(16), март 2004
Думаю, рассказывать об их назначении смысла нет. Если есть желание, вы можете выбрать гораздо больше пунктов, чем сделал я, и поставить себе на машину вдобавок ко всем, mplayer. Впрочем, для цели, ради которой мы все это затеяли, эти добавочные компоненты не критичны.
9
администрирование После того как установка завершится, файлы, скачанные из Сети, будут лежать в директории c:\Windows\ Windows Update Setup Files либо c:\Windows\Файлы установки Windows Update. Думаю, все понимают, что название директории зависит от языковой локализации Windows, использованной для скачивания. В директории скорее всего будут лежать файлы с такими именами: ADVAUTH.CAB IELPKAD.CAB README.CAB FONTSUP.CAB IE_S5.CAB VGX.CAB IEEXINST.CAB MPLAYER2.CAB filelist.dat IE_S3.CAB SWFLASH.CAB
FONTCORE.CAB IE_S4.CAB TS95.CAB IEDOM.CAB MOBILE95.CAB CRLUPD.CAB IE_S2.CAB SETUPW95.CAB HHUPD.CAB iesetup.dir
Несколько раз нажимаем кнопки «ОК» и «Далее», начинаем любоваться на индикатор установки. Иногда может случиться, что язык устанавливаемых компонентов отличается от того, что было в старых файлах.
ie6setup.exe iesetup.ini BRANDING.CAB IE_S1.CAB SCR56EN.CAB HELPCONT.CAB IE_S6.CAB Ýòó ïàïêó ìîæíî óäàëèòü.txt IE_EXTRA.CAB OAINST.CAB
Жизненно необходимы нам только вот эти файлы: IE_S1.CAB IE_S4.CAB IEDOM.CAB
IE_S2.CAB IE_S5.CAB SCR56EN.CAB
IE_S3.CAB IE_S6.CAB
Но все же лучше перестраховаться и на всякий случай скопировать все имеющиеся файлы во временную директорию Windows, которая у нас находится в $HOME/.cxoffice/ /dotwine/fake_windows/Temp. Распаковываем все файлы с расширением .CAB с помощью утилиты cabextract вот таким образом.
Бояться этого не стоит, просто нажмите кнопку «Да» и на следующий вопрос ответьте «Заменить все такие компоненты». Инсталляция весело побежит дальше и весьма благополучно доберется до этого момента:
#cabextract IE_S1.CAB IE_S2.CAB IE_S3.CAB
Утилита cabextract существует практически в каждом дистрибутиве Linux. Не стоит пытаться распаковать за один раз больше 3 файлов, иначе будете получать ошибки «Segmentation fault». Закончив с распаковкой, скопируйте файл nashbase.stf в файл acmsetup.stf и через программу officesetup, как обычно, начинаем инсталляцию Internet Explorer. Выбираем пункт Advanced install и указываем, где у нас находится файл acmsetup.exe. В ответ получаем вот такое весьма приятное для нас изображение.
10
указывающего на то, что все необходимые файлы скопированы и нужно просто перезапустить Windows. Нажимать на единственную доступную на этом диалоговом окне кнопку бесполезно. В ответ будем получать только вот такие ошибки.
администрирование И так будет продолжаться бесконечно. Дабы разорвать этот порочный круг, принудительно закрываем окно с работающей инсталляцией. Теперь нужно перезагрузить Windows, делается это с помощью пункта меню Simulate Windows Reboot или вызовом программы /opt/ cxoffice/bin/cxreboot. На экране будут наблюдаться следующие явления.
циями. В следующей статье мы рассмотрим методы и секреты борьбы с некоторыми особо упорными приложениями.
После окончания этого процесса можно будет спокойно работать с обновленным Internet Explorer. Последним шагом нужно будет установить DCOM98, требующийся для запуска большинства новых программ. Поэтому берем его тут: http://www.microsoft.com/com/dcom/ dcom98/download.asp и проводим стандартную инсталляцию. Я думаю, на сегодня достаточно трудов и мозговых усилий, пора и отдохнуть. Большинство программ будет отлично работать в конфигурации, созданной нашими манипуля-
№3(16), март 2004
11
администрирование
А ТЫ ЧТО ВИДИШЬ? УДАЛЕННОЕ УПРАВЛЕНИЕ ПОСРЕДСТВОМ RDESKTOP, RADMIN, VNC ... сейчас мы нажимаем на контакты и перемещаемся к вам, но если эта машинка не сработает, тогда уж вы с нами переместитесь, куда мы вас переместим... х/ф «Кин-Дза-Дза»
Время Время идет, идет, ваша ваша фирма фирма развивается. развивается. Появилась Появилась ЛВС, ЛВС, сначала сначала одноранговая, одноранговая, затем затем сс выделенными выделенными серверами. серверами. Хорошо, Хорошо, когда когда сеть сеть однородная, однородная, все все работают работают вв однотипной однотипной среде. среде. Но Но жизнь жизнь диктует диктует свои свои законы, законы, вв силу силу определенных определенных причин причин ЛВС ЛВС получилась получилась гетерогенной. гетерогенной. ИИ не не только только гетерогенной, гетерогенной, но но ии распределенной. распределенной. Представьте Представьте себе себе машиностроительный машиностроительный завод завод или или фирму фирму сс филиалами филиалами по по всему всему городу. городу. Что Что делать, делать, как как управлять управлять всем всем этим этим IT-богатством? IT-богатством?
АНТОН БОРИСОВ Можно бегать, как горная лань, по этажам и цехам, объяснять, разъяснять, учить. Можно нанять дополнительный персонал. Научить бегать его. Однако запомните, что если вы лично претендуете на звание системного администратора, то главная черта, которая вам должна быть присуща – это лень! В хорошем смысле этого слова (см. статью Вячеслава Калошина «Каким видится хороший системный администратор» в июльском номере журнала за 2003 г.). Рассмотрим, как снизить расходы «на перемещение в пространстве и времени» бренного тела администратора. Объяснять, как получить удаленное управление на UNIX-машинах я не буду. Считаю, что это известный факт. Цель этой статьи – показать, с помощью чего управлять с UNIX-машины парком серверов и рабочих станций с установленной ОС Windows. Начнем, пожалуй, с серверов. По всей видимости, у вас установлены следующие семейства – Windows NT Terminal Server 4.0, Windows 2000 Server/Advanced Server, Windows 2003 Server, Datacenter и т. п. Отлично, это даже нам на руку. Проверим, что установлены терминальные службы удаленного доступа (terminal services).
12
bash-2.05b# nmap -v -sS compaq
bash-2.05b# nmap -v -sS fuji
администрирование Чтобы узнать, какой именно порт отвечает за терминальные службы, поступим следующим образом: bash-2.05b$ cat /usr/share/nmap/nmap-services | grep Remote\ Display
А тем, у кого нет nmap, можно посмотреть весь список портов вот тут: http://www.iana.org/assignments/port-numbers. При установленной службе он будет присутствовать в системе. Можете на самом Windows-сервере запустить cmd.exe и посмотреть порты, которые использует сервер в данный момент: netstat -na | more (èëè netstat -na > netstat.txt)
Если портов с номером 3389 нет, то стоит добавить службу следующим образом: Пуск → Настройка → Добавление/Удаление Программ (Добавление Windowsкомпонентов). В итоге получили работающую терминальную службу. Что дальше, спросит любопытный читатель? Дальше сходим на сайт http://www.rdesktop.org и заберем исходники программы rdesktop. Она использует протокол RDP (т.е. протокол терминальной службы). Последняя версия 1.3 (на момент написания статьи). Настройка проста.
bash-2.05b$ ./configure --prefix=/usr/local/rdesktop bash-2.05b$ make bash-2.05b# make install
Теперь пакет rdesktop установлен. Осуществляем соединение с удаленным сервером. rdesktop rubin
Стоит отметить, что в некоторых случаях соединение может не заработать, а в ответ получим вот такую ошибку:
Проблема в том, что по умолчанию используется 5-я версия RDP-протокола, а некоторые сервера, не обращая внимания на все заплатки и обновления, продолжают говорить на 4-й версии. В таком случае нужно подать команду: rdesktop –4 rubin
На что выдается окно MSGINA для ввода имени пользователя, пароля, домена (если таковой имеется). Войдя в систему, мы работаем, как за родным Windowsтерминалом. Есть некоторые специфические моменты, когда нельзя использовать удаленный вход (но об этом в другой раз). В общем, аскетично, т.к. только 256 цветов, но вы ведь не играть пришли, верно?
13
администрирование Несколько слов об установке. Цель – получить у себя каталог, например, «RAdmin» со следующими файлами:
Во времена, когда администраторы были еще белыми и пушистыми, активную популярность приобрел пакет Remote Administrator. Стоит отметить, что программа на момент своего появления на свет затмила свои аналоги. Одна из причин, из-за чего программу устанавливают, – это возможность показать оператору, работающему за удаленной машиной, что именно у него нажимается не так, ибо перехватывается управление как клавиатурой, так и мышкой. Среди дополнительных возможностей программы – наличие в некоторой степени аналога ftp-сервиса. Авторизовавшись на удаленной машине, появляется возможность перебросить туда файл с нашей локальной машины. Еще один немаловажный момент – при нарушении настроек, например, swap-файл не прописался, удаленный/локальный пользователь не может зайти, т.к. окно приглашения не появляется. Что делать? Выбираем режим работы с удаленной машиной «File transfer» и удаляем неправильный swap-файл. Но! Данный продукт написан под Windows-платформу. По заявлениям самих разработчиков, версию под UNIXсистемы они разрабатывают, но как скоро она появится и появится ли вообще – неизвестно. Переустанавливать на всех клиентах что-то альтернативное, чтобы администратор мог со своей машины без затруднений управлять всем парком, – лишняя беготня. Нужна ли она? Как увидим чуть далее – нет. Что нам потребуется для запуска? Windows-эмулятор, он же wine. Пакет, если у вас его нет, заберем на http://www.winehq.com. Настройка аналогична настройке rdesktop. Никаких дополнительных опций я не задавал. Префикс пути, где будет лежать пакет: --prefix=/usr/local/wine
Обращаю ваше внимание, что пакет громоздкий – потребуется для сборки порядка 500 Мб. Плюс еще 260 Мб для готового к употреблению изделия. Итак, собрали пакет, научились его запускать на простейших Windows-играх, например, «Сапер», «Паук» и т. д. Теперь устанавливаем у себя на Linux-машине Remote Administrator.
14
Теперь приступим непосредственно к запуску radmin.exe. В домашей директории есть каталог, относящийся к wine – ~/.wine, где лежит файл конфигурации «config». Для того чтобы в дальнейшем не было проблем с фокусировкой мышки и клавиатурного ввода, добавим следующие строчки: [AppDefaults\\radmin.exe\\x11drv] "Desktop" = "700x500" "Managed" = "N"
Строка «Desktop» = «700x500» означает, что для приложения radmin.exe необходимо использовать окно с геометрией 700 на 500 точек. Облегчает поиск нашего приложения radmin.exe, когда используется разный тип изменения фокуса в оконном менеджере. Из рисунка видно, что открываемое приложение не будет максимизировано оконным менеджером, а будет открыто с указанными размерами «горизонталь на вертикаль». Строка «Managed» = «N» приводит к тому, что изменить размеры не получится. В любом случае смотрите документацию к wine. Теперь мы готовы к запуску. /usr/local/wine/bin/wine ~/RAdmin/radmin.exe
Подразумевается, что мы установили пакет в каталог RAdmin. Далее введем регистрационный ключ и, вуаля, далее вопрос техники – заполнить имена хостов. С этого момента у нас полноценный доступ к другим машинам с установленной radmin-службой. Скажу несколько слов об обратной связи. Запустив на Linux-машине radmin-сервис: /usr/local/wine/bin/wine ~/RAdmin/r_server.exe
с Windows-машин мы можем видеть рабочий экран Linuxмашины. Правда, управлять нельзя, можно только наблюдать. Это лирическое отступление. Замечены некоторые проблемы с клавиатурой, а именно при нажатии комбинаций клавиш Shift+Key проявляется эффект «залипания» Shift. С чем это связано, неизвестно. Известно, как от этого избавиться. Нажать на настройке окна radmin.exe «Options», и залипание пропадет. И напоследок посмотрим, что такое VNC. VNC – Virtual Network Computing, пакет разрабатывался в лаборатории AT&T, сейчас доступен в свободной форме на сайте http://www.realvnc.com. Богатый ассортимент настроек, автоматическое определение необхо-
администрирование димой ширины канала для передачи информации и т. п. Например, в любой момент времени можно переключиться из TrueColor-режима в режим 256 цветов. Экономия на трафике. Работа с использованием VNC-клиента с сервером аналогична двум предыдущим рассмотренным пакетам. Нажимая F8, получаем возможность изменить на ходу настройки. Нажатие первый раз обрабатывает наш VNCклиент, которого мы запустили у себя, второй раз уже обрабатывает сервер, где запущена VNC-служба. Службу можно определить по открытым портам 5800, 5900. В качестве эксперимента я поставил VNC у себя в локальной сети на Linux-сервер под своим аккаунтом. Так что при перезагрузке вторая моя машина запускает автоматически VNC-сервис. Повторю, что поставил в качестве эксперимента, ибо никто не отменял комбинацию со вторым X-сервером и перенаправлением ввода с удаленной UNIX-машины на этот второй X-сервер. Тем не менее опишу, как я провел сию операцию. Обе машины в локальной сети работают под slackware linux, поэтому настройки, которые я добавлял, находятся в каталоге /etc/rc.d/. Добавляю туда скрипт rc.vnc. #!/bin/sh # Start the Virtual Networking Communication (VNC) server BINDIR=/usr/local/vnc DAEMON=vncserver case "$1" in 'start')
su - anthony -c "export PATH=$PATH:/usr/X11R6/bin:/usr/ ↵ local/bin; $BINDIR/$DAEMON :10 -geometry 800x600" ;; 'stop') $BINDIR/$DAEMON -kill :10 ;; 'restart') $0 stop; $0 start ;; *) echo "usage $0 start|stop|restart" ;; esac
Для запуска нового сервиса сделаем упоминание также в файле /etc/rc.d/rc.local. Добавим строчки: echo "***** Starting RealVNC server *****" /etc/rc.d/rc.vnc start
Появляющийся сервис запускается с правами пользователя anthony, который присутствует в системе. Экспортирование пути необходимо, т.к. при запуске «su» автоматически переменная PATH заполняется значениями из /etc/login.defs. Мы же чуть-чуть увеличим путь, т.к. для запуска Xvnc необходимо, чтобы vncserver знал, где его найти, а также некоторые дополнительные программы. Сервис запускается с виртуальным экраном номер 10, поэтому для его обслуживания видим сразу 3 открытых для нужд VNC порта – это 5810, 5910 и 6010. Полная карта открытых портов на этой машине приведена ниже. bash-2.05b# nmap -v -sS fuji -p 1-10000
15
администрирование
Чтобы запустить клиента, пишу следующую строчку: vncviewer fuji:10 -passwd ~/.vnc/passwd -geometry 600x400
где fuji:10 – linux-box, где установлен VNC-сервис на 10 вир-
16
туальном экране, с помощью параметра «-geometry 600x400» указываю, что клиент надо запустить с разрешением 600 на 400 точек. Параметр «-passwd ~/.vnc/passwd» означает, что пароль можно не вводить, а брать по указанному месту. Кстати, он скопирован с машины, где запущен VNC-сервис. Вот вроде бы и все. Работа с помощью VNC немного удобнее по сравнению с остальными. Хотя это мой субъективный взгляд. В качестве примера рассмотрим, как посчитать трафик, используемый каждым приложением. Я опишу, как на стомегабитной сетевой карте добиться скорости 9600 байт в cекунду. Сия скоростная планка взята из желания посмотреть, насколько устойчиво будут работать рассмотренные приложения. Не секрет, что до сих пор в некоторых местах используются телефонные линии с не очень хорошим качеством связи. Вы можете указать свою скорость, отличную от указанной, и проанализировать ситуацию. Считаю, что данный опыт будет полезен в случае, когда организация стоит перед выбором, какой тип соединения использовать для связи филиалов – ТФОП (телефонные линии общего доступа), беспроводной доступ или иной вид связи. Проанализировав на локальной сети центрального филиала, что именно подходит под ваши условия, вы делаете свой выбор. Чтобы «зажать» скорость в указанных пределах, я воспользовался traffic shaper – rshaper. Взять можно по указанному адресу: http://ar.linux.it/software/#rshaper. Сборка происходит через подачу команды make && make install. Пакет устанавливается в каталог /usr/local/sbin как модуль к ядру и программа управления трафиком.
администрирование В целом происходит следующее – данный модуль создает сетевой буфер, где пакеты задерживаются определенное время, в результате чего создается эффект «простаивания в очереди», т.е. создается определенная скорость. В нашем случае 9600 б/с на прием. Нам еще потребуется, чтобы у вас в системе также был простейший брандмауэр, точнее настроены правила для iptables. Например, взятый здесь: http://www.faqs.org/docs/ iptables/examplecode.html. Сначала подгружаем shape-модуль: modprobe rshaper.o
Модуль загружен, далее указываем, с каким хостом мы соединяемся, с какой скоростью и размер простаивания в буфере (в секундах): rshaperctl HOST SPEED TIME
Затем выставляем правила для iptables для подсчета прошедшего трафика. Для входящего от хоста трафика: iptables -N MyRDP_IN iptables -I INPUT -j MyRDP_IN -s HOST iptables -I MyRDP_IN -j INPUT
Для исходящего к хосту трафика: iptables -N MyRDP_OUT iptables -I OUTPUT -j MyRDP_OUT -d HOST iptables -I MyRDP_OUT -j OUTPUT
Пролистать показания счетчика: iptables -L MyRDP_IN -v
С теорией достаточно. Ближе к практике. Для этих целей составлен простейший скрипт, в котором задается скорость для shaper, происходит обнуление/показ прошедшего трафика. MyRDPTest.sh #!/bin/sh IPTL=/usr/sbin/iptables SHAPER=/usr/local/sbin/rshaperctl MODE=9600 HOST=rubin TIME=10 if [ "$1" = "zero" ]; then echo "Zeroing IN & OUT counters for $HOST"; $IPTL -L MyRDP -Z 2>/dev/null 1>/dev/null $IPTL -L MyRDPOut -Z 2>/dev/null 1>/dev/null fi echo "Setting Shaping MODE as $MODE bytes to host $HOST" $SHAPER $HOST $MODE $TIME $SHAPER echo "Incoming traffic from host" IN=`$IPTL -L MyRDP -v -n | grep ACCEPT` INpkts=`echo $IN | awk '{ print $1 }'` INbytes=`echo $IN | awk '{ print $2 }'` echo "pkts = $INpkts, bytes = $INbytes"; echo "Outcoming traffic to host" OUT=`$IPTL -L MyRDPOut -v -n | grep ACCEPT` OUTpkts=`echo $OUT | awk '{ print $1 }'` OUTbytes=`echo $OUT | awk '{ print $2 }'` echo "pkts = $OUTpkts, bytes = $OUTbytes";
17
администрирование Если скрипт вызван с параметром «zero», то счетчики для входящего (MyRDP) и исходящего (MyRDPOut) трафика будут обнулены. Вот какие цифры получились у меня после недолгой работы по разным протоколам с сервером по имени RUBIN. Скорость на прием – 9600 байт в секунду.
и VNC легче держат канал. Первый даже лучше, нежели VNC. Может на других скоростях получатся другие цифры, но это испытание мы оставим пытливому читателю. Òàáëèöà 1. Èíôîðìàöèÿ î ïðîøåäøåì òðàôèêå çà 5-ìèíóòíûé ïåðèîä ïî ðàçíûì ïðîòîêîëàì
RDP ïðîòîêîë Incoming traffic from host pkts = 2604, bytes = 1039K Outcoming traffic to host pkts = 3563, bytes = 474K VNC ïðîòîêîë Incoming traffic from host pkts = 6277, bytes = 804K Outcoming traffic to host pkts = 5413, bytes = 321K RADMIN ïðîòîêîë Incoming traffic from host pkts = 2739, bytes = 472K Outcoming traffic to host pkts = 3610, bytes = 267K
Проводились типизированные действия. В частности, был запущен MPEG-клип через Media Player. Что следует отметить – при работе с RDP передавался n-ый кадр с заметными паузами для перерисовки. Остальные протоколы автоматически пропускали прорисовку кадров, простонапросто ничего не рисовали, т.е. старались не занимать канал «тяжелым» содержимым. Это касается только видео. В остальном субъективно было заметно, что RADMIN
18
Я специально не стал освещать решения от компании Citrix, поскольку это отдельный серьезный разговор, т.к. заложенных возможностей в данный продукт хватит надолго и намного. P.S. Всегда были и будут разговоры об эффективности и применимости удаленного доступа и открытых для этих целей дверей в системе. Приходится чем-то жертвовать, либо потенциально открыто, либо потенциально закрыто. Запомните, самый оптимальный с точки зрения безопасности стиль работы – держать администрируемую машину всегда выключенной. В статье были упомянуты программы со следующих ресурсов: 1. http://www.rdesktop.com 2. http://www.radmin.com 3. http://www.winehq.com 4. http://www.realvnc.com 5. http://ar.linux.it/software/#rshaper 6. http://www.faqs.org/docs/iptables/examplecode.html
администрирование
19
администрирование
СВОБОДНАЯ ДОС ДЛЯ СВОБОДНЫХ ЛЮДЕЙ, ИЛИ НЕ LINUX ЕДИНЫМ ЖИВ ЧЕЛОВЕК
Когда говорят об операционной системе, обозначаемой аббревиатурой DOS, мало кто задумывается – о какой же Дисковой Операционной Системе (Disk Operation System) идет речь. Обозначение целого класса операционных систем для большинства людей стало синонимом лишь одного-единственного его представителя – MS DOS фирмы Microsoft. Кто-то, может быть, вспомнит PC DOS, отличающуюся в основном лишь названием и именами некоторых файлов. Может, кому-то придет в голову и DR-DOS от компании Digital Research – создателя предшественницы DOS от Microsoft, операционной системы CP/M. Но в целом почти для всех DOS – это MS-DOS, последняя версия которой вышла вот уже десять лет назад, и которая давно завершила свое развитие.
АНДРЕЙ МАРКЕЛОВ 20
администрирование Однако до сих пор в эксплуатации остается огромное число программ, работающих в среде и написанных под эту нетребовательную к ресурсам ОС, и не меньшее число морально устаревших компьютеров, прекрасно работающих под ней. Как же быть? Ведь MS-DOS давно не поддерживается и не продается. Я бы посоветовал обратить внимание на FreeDOS, изначально написанную Джимом Холлом, а сейчас развивающуюся при участии целой команды разработчиков из разных концов света. Свободная ОС FreeDOS была анонсирована ее создателем Джимом Холлом (Jim Hall) 28 июня 1994 года первоначально под именем PD-DOS. Уже в июле приставка «PD-» была заменена на «Free-». Знак «дефис» между двумя составляющими названия операционная система потеряла в 1996 году при достаточно курьезных обстоятельствах. В этом году издательство «R+D Books» выпускало книгу под названием «Free-DOS Kernel», и редактор издательства выбросил знак «дефис» в названии исключительно из дизайнерских соображений. Основной причиной, вызвавшей появление проекта, Джим называет прекращение компанией Microsoft поддержки операционной системы MS-DOS. Итак, вот уже 10 лет мы имеем реальную альтернативу операционной системе MS-DOS, но под лицензией GNU. «FreeDOS Project» не использует код, созданный Microsoft. Согласно открытым спецификациям команда пишет свой код, обладающий аналогичным функционалом. Ядром FreeDOS является DOS-C, изначально написанное Pat Villani как DOS-ядро для встраиваемых систем. Первоначальное название – DOS/NT. DOS/NT содержала 32 000 строк кода, была написана на Cи и ассемблере и распространялась как shareware. FreeDOS работает на устаревшем железе (начиная от 5МГц IBM PC XT с 640К оперативной памяти), встроенных системах, различных виртуальных машинах, в том числе DOSEmu, VMWare и Bochs. FreeDOS – идеальное, лицензионно чистое решение для создания «спасательной» загрузочной дискеты. Другое применение – среда для исполнения ваших программ или обновлений. Клиенту достаточно загрузиться с полученной от вас дискеты или компакт-диска, и вот вам (или вашей службе поддержки) уже не приходится часами висеть на телефоне, объясняя бухгалтеру, находящемуся за несколько сотен километров, как найти диск C:\. FreeDOS обладает отличной совместимостью с DOSпрограммами, в том числе со старыми добрыми играми: DOOM, Quake, Warcraft 2. А небезызвестная фирма Dell даже продает свои десктопы, с предустанавливаемой на них одной из версий этой операционной системы. Из особенностей FreeDOS я хочу отметить: поддержку FAT-32 дисков объемом до 128 Гб, поддержку сети (вы можете поставить на FreeDOS ftp- и HTTP-сервер), но отсутствие встроенной поддержки NTFS и USB. Однако FreeDOS вполне нормально работает и с USB-клавиатурами, USB-мышами, Serial-ATA-дисками, если их поддерживает BIOS компьютера. При помощи дополнительных драйверов возможна работа с длинными именами.
№3(16), март 2004
Установка Итак, вы решили познакомиться с новой для себя операционной системой. Прекрасно! Пятидесятимегабайтный дистрибутив «FreeDOS Beta9 pre-release 3» (последний на момент написания статьи) скачан в виде ISO-образа с сайта http://www.freedos.org и записан на «болванку». Вставляем полученный загрузочный CD в лоток вашего CDROM, и перезагружаем компьютер. Не забудьте попутно в качестве устройства для загрузки выбрать привод компакт-дисков.
Я буду описывать установку FreeDOS на «чистую» машину, однако никто не мешает использовать так называемую «двойную загрузку». Я успешно ставил на один компьютер одновременно MS-DOS, FreeDOS, Linux и Windows 2000. Причем в качестве загрузчика использовал штатный Boot Loader из Windows 2000. При помощи отличной утилиты BootPart 2.50 вы можете сохранить образ 512-байтного загрузочного сектора с загрузчиком FreeDOS, Lilo или GRUB в файл, а далее просто прописать на него ссылку в C:\BOOT.INI. За подробностями отсылаю вас на домашнюю страничку программы – http://ourworld.compuserve.com/homepages/ gvollant/bootpart.htm. Кроме того, всегда можно попробовать запустить FreeDOS в среде виртуальной машины. Нужно заметить, что для DOSEmu рассматриваемая операционная система и так является «официальной» и рекомендуемой к использованию. Загрузившись с дистрибутивного диска, мы попадаем в меню инсталлятора. Нажимаем «1» для старта. Далее выбираем установку с использованием драйвера CD-ROM и XMS – «2». На машинах с процессорами 8086 – 80286 нужно выбрать «1». В следующем меню имеется несколько вариантов: ! «1» – установка; ! «2» – переход в командную строку; ! «3» – создать загрузочную дискету. Выбираем «1» и еще раз «1», чтобы подтвердить установки по умолчанию. Теперь мы попадаем в меню «FreeDOS Partition Management». Eсли жесткий диск не был разбит на логические диски, то сейчас имеется возможность разбить его аналогом MS-DOS-утилиты FDISK. Кроме того, можно запус-
21
администрирование тить известную всем линуксоидам утилиту для изменения размеров партиций – FIPS, а также отформатировать диск. Замечу, что утилита FORMAT дистрибутива содержит ошибки (которые исправлены в более поздней версии, не вошедшей в ISO-образ), и при наличии ошибок во время форматирования лучше выполнить данную операцию из другой ОС, а FORMAT после инсталляции заменить более свежей версией с сайта проекта. Еще один вариант решения этой проблемы – выполнить «быстрое» форматирование FORMAT из FreeDOS. Если уже имеется отформатированный диск, то просто выбираем его клавишами со стрелками и нажимаем «Enter». После инсталлятор предлагает выбрать вариант установки – графический или текстовый. Выбрав, жмем «Enter» и в случае с графикой оказываемся в интерфейсе, напоминающем интерфейс установки обычного Windows-приложения. Принимаем лицензионное соглашение GNU GPL, указываем путь для установки (C:\FDOS\) и набор устанавливаемых пакетов. Теперь остается только следить за ходом инсталляции. После завершения копирования файлов будут запущены несколько конфигурационных скриптов, а затем мы попадаем в командную строку, получив напоминание о необходимости записать загрузочный сектор командой BOOT. Набираем «boot», жмем «Enter». Теперь можно перезагрузить машину, удалив при этом CD из дисковода.
Настраиваем среду обитания Вначале кратко о структуре каталогов и файлах, входящих в дистрибутив. После установки FreeDOS в корне диска C:\ мы имеем: ! autoexec.bat, config.sys – расширенные по синтаксису аналоги конфигурационных файлов MS-DOS; ! fdosboot.bin – загрузочный сектор FreeDOS в виде файла; ! command.com – интерфейс командной строки; ! kernel.sys – ядро операционной системы (аналог msdos.sys). Сами служебные файлы и утилиты по умолчанию устанавливаются в C:\FDOS. Внутри каталога существуют следующие подкаталоги: ! APPINFO – файлы формата lsm с кратким описанием утилит дистрибутива; ! BIN – утилиты и драйверы; ! DOC, HELP – документация ; ! INSTBASE – логи инсталляции всех пакетов; ! NLS – файлы локализации для нескольких языков. Первым делом я бы рекомендовал обновить файлы операционной системы. Главные кандидаты на обновление (из тех, что были доработаны по сравнению с «FreeDOS Beta9 pre-release 3») – это новая версия ядра под номером 2033, FreeCOM shell – интерфейс командной строки, EMM386, Format, Shsucdx, Undelete, Edit. Ссылки на утилиты и файлы для скачивания можно найти на сайте проекта. Для обновления, как правило, достаточно просто заменить старые файлы новыми из скачанного zipархива. Когда будете обновлять файлы ядра, заметьте,
22
что бинарники ядра распространяются в двух вариантах: keXXXX_32.zip – с поддержкой FAT-32 и keXXXX_16.zip – только с поддержкой FAT-16. Затем убедимся, что доступен привод компакт-дисков. Файл config.sys должен содержать строку: DEVICE=C:\FDOS\bin\atapicdd.sys /D:FDCD0001 À autoexec.bat êîìàíäó: C:\FDOS\bin\Shsucdx /D:FDCD0001
Как видно, синтаксис этих команд не отличается от синтаксиса команд Microsoft DOS. Отличия только в именах файлов. Drugim vagnjm voprosom jvljaetsja russifikazcija. В текущей версии отсутствует поддержка COUNTRY, но для поддержки 866 кодовой страницы можно воспользоваться GRAFTABL. С другой стороны, самым простым решением будет использование (до появления полноценной поддержки русского языка «из коробки») одного из русификаторов: Keyrus или rc. Они прекрасно служили нам в MS-DOS, с таким же успехом послужат и во FreeDOS. Следующая часто возникающая задача – доступ к томам NTFS. В ядре FreeDOS отсутствует поддержка NTFS, но по ссылке http://www.sysinternals.com/ntw2k/freeware/ntfsdos.shtml можно скачать бесплатную версию NTFSDOS – драйверов, работающих под FreeDOS и предоставляющих доступ к томам NTFS в режиме «только для чтения». Существует также платная Professional-версия с возможностью записи. Использовать программу крайне просто. Добавьте вызов файла ntfsdos.exe в файл autuexec.bat и утилита сама просканирует доступные диски и, если на них будут найдены тома NTFS, подключит их без вашего вмешательства. Теперь перейдем к «длинным», выходящим за рамки формулы 8+3 именам файлов, которые впервые появились в ОС Windows 95 SR2. Существует несколько утилит, созданных для поддержки длинных имен. Например, пакет DOSLFN, который можно скачать по адресу http:// www-user.tu-chemnitz.de/~heha/hs_freeware/freew.html. Прописываем в autoexec.bat вызов TSR-модуля doslfn.com, который занимает 16 Кб в оперативной памяти, и на этом вся установка закончена. Потенциальной проблемой может стать то, что поддерживаются длинные имена не на всех приводах CD-ROM. В случае возникновения таких проблем можно попробовать еще один пакет, выполняющий аналогичные функции, – LFN Tools (http://www.odi.ch/).
Графические оболочки Любые задачи в FreeDOS можно выполнить, не выходя за рамки командной строки. Но, конечно, намного удобнее использовать одну из многих так называемых «оболочек» – shells. Хочу напомнить, что та же MS Windows вплоть до версии Windows 3.11 for Workgroups являлась ничем иным, как оболочкой для MS-DOS. И лишь Windows 95 присвоила себе гордое имя операционной системы. Говоря об оболочках, в первую очередь упомяну оболочку – бессмертный файловый менеджер Norton Commander и его многочисленные клоны, например, миниатюрный Volkov Commander и менеджер с открытым исходным кодом Dos Navigator (http://www.ritlabs.com/dn/).
администрирование
Вышеперечисленные программы имеют текстовый интерфейс на основе псевдографики. Гораздо интереснее познакомиться с по-настоящему графическими оболочками. Часть из тех оболочек, что работают в среде FreeDOS, перечислена в таблице. Очень кратко рассмотрим некоторые из них. SEAL – тридцатидвухразрядная оболочка, напоминающая по интерфейсу MS Windows. В составе пакета имеется некий минимальный набор приложений, включая текстовый редактор, среду разработки, графический редактор, файловый менеджер, CD-плейер, программу снятия образа с дискет, более десятка игр. Для инсталляции достаточно разархивировать скачанный с сайта http:// sealsystem.sourceforge.net/ архив и запустить install.exe. Оболочка запускается командой C:\seal2\seal.exe.
OpenGEM – развитие Digital Research GEM под открытой лицензией GPL. OpenGEM достаточно большой па-
№3(16), март 2004
кет – на жестком диске требуется почти 10 Мб. Есть версия, помещающаяся на дискете – GEMini. Кратко по установке. После того как дистрибутив скачан и разархивирован, запускаем install.bat. Программа установщика задаст несколько вопросов: куда ставить пакет и имеется ли на компьютере установленная Windows. После отработки «батника» добавляем строки из файла C:\fgconfig.sys в config.sys. Запускается оболочка командой C:\gem.bat. Интерфейс OpenGEM изображен на следующем рисунке:
В состав пакета входит более 30 приложений. В том числе текстовый процессор, электронные таблицы, HTMLбраузер, игры. На этом все. Остались нераскрытыми многие темы, относящиеся к FreeDOS, в том числе создание своего дистрибутива и поддержка сетевых служб. Но это уже материал отдельной статьи.
23
администрирование
С ЮНИКСОМ НА vi
СЕРГЕЙ СУПРУНОВ 24
администрирование Так уж исторически сложилось, что операционные системы UNIX считаются очень сложными и недружественными по отношению к пользователям. И одним из олицетворений этого часто называют редактор vi. По мере того, как набирает обороты Linux, позиционируемый как система более дружественная, среди его пользователей все большую популярность приобретают более привычные редакторы, особенно тот, который встроен в Midnight Commander (поскольку всегда под рукой), и старый добрый vi начинает забываться. Тем не менее, этот редактор обладает непревзойденной на сегодняшний день мощью, функциональностью, универсальностью и удобством работы, а кроме того, разработан в полном соответствии с идеологией UNIX. Чтобы не быть голословным, приведу несколько примеров (синтаксис команд редактора, которые в них встретятся, будет подробнее рассмотрен ниже): ! Задача: в середину некоторой статьи нужно вставить вывод команды «ipfw show» (или любой другой). Попробуйте сделать это, используя ваш любимый редактор, а затем посмотрите, как это делается в vi. Конечно же, будучи квалифицированным пользователем, вы не стали вбивать результат вручную, а перенаправили вывод в файл, который объединили с файлом статьи: # ipfw show > buffer # cat article buffer > temp # mv temp article
после чего, воспользовавшись функциями редактирования блоков (ваш же редактор позволяет это?), перенесли блок в нужное место. Конечно, с помощью Midnight Commander можно сделать то же самое проще: # ipfw show > buffer # mcedit article
Идем в точку, в которую нужно вставить блок, затем жмем F9 и выбираем в меню «Файл» пункт «Вставить файл…», указываем путь к файлу buffer. Ну а с помощью vi это выполняется одной командой: !lipfw show
! Предположим, что у вас есть пронумерованный спи-
!
сок из 12 пунктов. Вам нужно вставить два пункта, начиная с третьей позиции, и соответственно поправить номера всех нижележащих пунктов. Наверняка решать эту задачу вам придется руками. Vi позволяет сократить число операций до минимума. Для первого пункта списка, номер которого нужно поменять, ставим курсор на его номер и даем команду «2#+», после чего на номере каждого последующего пункта просто давим символ «.». Двадцать четыре строки в тексте нужно сдвинуть вправо символом табуляции. Конечно, не проблема минутку пощелкать «Tab – Стрелка вниз – Home», но vi предлагает более элегантный способ: «23>j» над первой строкой. И все.
№3(16), март 2004
Надеюсь, мне удалось убедить вас в том, что vi – один из лучших текстовых редакторов для систем UNIX. Ниже мы более подробно рассмотрим его особенности, синтаксис некоторых команд, коротко коснемся режимов ex и view. Чтобы учесть интересы читателей с разным уровнем подготовки, данная статья будет разбита на следующие подразделы: ! основы редактора vi; ! команды перемещения и поиска; ! команды редактирования и форматирования; ! работа с блоками и буфером; ! команды режима редактирования; ! прочие команды режима visual; ! команды режима ex; ! установки редактора; ! заключение. Мне не хочется перегружать статью снимками экранов, поэтому, если вы не очень хорошо знакомы с vi, настоятельно рекомендую вам читать ее перед монитором и сразу пробовать те примеры, которые будут описаны ниже, чтобы «почувствовать» этот редактор.
Основы редактора vi Итак, vi – текстовый редактор, входящий в поставку практически всех операционных систем семейства UNIX. Запуск его осуществляется командой: # vi [filename]
Помимо основного (visual) режима он может быть запущен в командном режиме, называемом ex (от англ. execute), ориентированном на работу со строками, и в режиме view (только для чтения). Запуск этих режимов осуществляется с ключами –e и –R, или командами ex и view соответственно: # {vi –e | ex} [filename] # {vi –R | view} [filename]
Если файл с таким именем существует в текущей директории, то он будет открыт для редактирования. Ну а если его нет, то будет создан новый файл. Если команда vi дана без параметров, будет запущено редактирование нового файла, создаваемого во временном каталоге, заданном переменной окружения TMPDIR (обычно это папка /tmp) с именем типа vi.L14259 (часть после точки создается случайным образом, чтобы обеспечить уникальность создаваемого файла). В дальнейшем вы сможете сохранить набранный файл под любым именем. Если вы последовали моей рекомендации и сразу проверяете все на практике, то первое, чему вы должны научиться, это выходить из редактора. Дело в том, что в vi вам не помогут ни <escape>, ни <Alt+X>, ни даже <Ctrl+C>. Нет в нем и так любимой народом верхней строчки, именуемой «Главное меню». Также он не будет отображать то, что вы попытаетесь набрать (хотя, если в панике начать беспорядочно стучать по клавиатуре, то может случиться «чудо» и ваш набор станет появляться на экране, однако выйти из редактора это вам все равно не помо-
25
администрирование жет). Так что абсолютно черный экран с одинокими тильдами по левому краю, никак не реагирующий на нажатия клавиш и не позволяющий вернуться в оболочку иначе, как «килянием» процесса из соседнего терминала (если вы, конечно, умеете пользоваться командой kill – иначе только Reset), может надолго отбить охоту запустить этот редактор еще раз. Поэтому сначала ознакомьтесь со следующей таблицей, которая поможет вам поверить в то, что vi – это действительно редактор:
Команды, начинающиеся с символа «:», будут отображаться в нижней строке. Остальные выполняются «молча». Редактор vi имеет два режима работы – режим команд и режим редактирования. Запускается он в командном режиме, так что все нажатия на клавиши трактуются как команды. Нажатие клавиш «i», «a», «o», «O» и ряд других переводят vi в режим вставки, когда набираемые символы трактуются как текст и отображаются на экране. Возврат к режиму команд выполняется клавишей <escape> или в некоторых случаях автоматически, например, при попытке передвинуть курсор левее первого символа в строке (в редакторе vim, являющемся модернизированным вариантом vi и часто заменяющем его в Linux, в этом случае редактор остается в режиме вставки). Автоматический переход в командный режим обычно сопровождается звуковым сигналом, как и ошибочная команда. Чтобы почувствовать все это, выполним небольшое практическое упражнение. Находясь в своем домашнем каталоге, запустите редактор командой: # vi test.txt
Далее нажмите «i», чтобы перейти в режим вставки. Теперь все нажатия на клавиши будут трактоваться как ввод текста, и символы будут отображаться на экране с позиции курсора. Наберите «Hello, world!». Постарайтесь не ошибаться, поскольку исправление ошибок, как и все остальное, имеет здесь свои особенности, о которых мы поговорим ниже. Нажмите <escape> для возврата в командный режим. Наберите «:wq» и нажмите <enter>. Убедитесь, что файл test.txt действительно создался. После этого небольшого задания вам будет проще представлять то, о чем пойдет речь далее. Аналогично команде «i», в режим вставки можно перейти, нажав клавишу «a». Единственное отличие – текст будет вставляться не перед символом, на котором нахо-
26
дится курсор, а после него. Кроме того, режим вставки может быть вызван командами «o» и «O». Первая из них добавляет пустую строку после, а вторая – перед текущей строкой, и дальнейший ввод символов трактуется как ввод текста. Чтобы удалить символ, нужно перейти в режим команд и над удаляемым символом нажать клавишу «x». В режиме вставки удалить только что введенный ошибочно символ можно клавишей <backspace>, однако в vi таким образом может быть удалена только последняя непрерывно введенная последовательность символов. То есть если вы откроете для редактирования наш тестовый файл со строкой «Hello, world», и добавите между словами слово «my»: «Hello, my world», то, используя клавишу <backspace>, вы сможете удалить только что введенные символы « my», а вот запятую и последующие символы удалить таким образом уже не удастся. В этом случае придется использовать команду «x». Удалить целиком строку, на которой находится курсор, можно командой «dd» (просто нажмите два раза клавишу <d>). Помните, что в vi строкой считается не экранная строка, а последовательность символов до перевода строки (\n). Если строка больше 80 символов (значение по умолчанию), то она переносится на новую линию (строку экрана). Используя «dd», вы удалите всю строку вне зависимости от того, на скольких экранных линиях она размещается. Чтобы определить, где находится конец строки, нажмите клавишу «$» (вы, должно быть, уже заметили, что клавиши <Home>, <End> и т. п. тут не работают). При навигации по экрану ( можно пользоваться стрелками, хотя есть и более «правильный» способ) курсор перемещается не по экранным линиям, а по строкам текста. Если вы что-то сделали не так, то отменить последнюю операцию можно командой «u». Эта команда отменяет только последнее действие, то есть ее повторное применение отменит только что сделанную отмену. Конечно, отсутствие истории операций – один из серьезных недостатков vi, однако если работать вдумчиво и внимательно, то он почти незаметен. Собственно, идеология UNIX-систем такова, что они не особо балуют пользователя подсказками и возможностью отката выполненных операций, так что сразу настраивайтесь на ответственную работу. Учтите, что по команде «u» отменяется вся команда целиком. Например, если вы дали команду «i», набили 3-й том «Войны и мира» и вернулись в режим команд, то «u» отменит весь этот ввод. Если ошибок получилось слишком много, можно выйти из редактора без сохранения сделанных изменений (команда «:q!»). В принципе этих сведений достаточно, чтобы отредактировать файл. Однако вряд ли вы поверили, что vi – удобный редактор. Понимаю, как вам хочется нажать <F4> в своем любимом mc, но все же найдите в себе силы дочитать статью до конца и попрактиковаться с vi хотя бы пару недель. Думаю, ваше мнение изменится. В дальнейшей части статьи некоторые команды и их применение будут рассмотрены более подробно. За описанием всех остальных команд отсылаю читателя к стра-
администрирование ницам справочного руководства «man vi», в полном соответствии с идеологией UNIX.
Команды перемещения и поиска Эта часть статьи посвящена командам навигации, которые позволят вам быстро и эффективно перемещаться по редактируемому тексту. Как и все в vi, эти команды также имеют свою специфику, и их удобство и продуманность начинают осознаваться только через несколько месяцев работы. Хотя и новичок найдет среди них полезные и интересные. В общем случае по тексту можно перемещаться, используя клавиши со стрелками. Но vi разработан таким образом, чтобы в процессе работы руки не покидали основной рабочей зоны клавиатуры. Те, кто владеет методом «слепой» печати, по достоинству оценят эту особенность. Кроме того, это позволяет не заботиться о совместимости клавиатур и терминалов – vi будет полностью функционален даже на самых тупых терминалах. Команды навигации и поиска представлены ниже. Запись «№» означает число, которое набирается перед вводом команды (при этом на экране оно не отображается) и задает число повторений команды или в некоторых случаях номер строки, к которой команда должна быть применена. Если число не задано, команда выполняется один раз.
Команды перемещения курсора ! [№]h – перемещает курсор влево на № символов; ! [№]k – перемещает курсор на № символов вверх; ! [№]j – перемещает курсор вниз на № символов; ! [№]l, [№]<пробел> – перемещает курсор на № симво! ! ! ! ! !
лов вправо; [№]$ – переход на последний символ строки, №-й после текущей; 0 (ноль) – переход на первый символ текущей строки; ^ – перемещает курсор на первый символ текущей строки, отличный от пробельного; [№]- – переход на первый непробельный символ №-й перед текущей строки; [№]+ – переход на первый непробельный символ №-й после текущей строки; [№]_ – переход на первый непробельный символ (№-1)-й после текущей строки.
Также существует масса команд для перемещения по словам, группам символов, предложениям, параграфам. При этом под «словом» будет пониматься последовательность символов, разделенных пробельными символами. Термином «группа символов» будем именовать последовательность символов, не разделенных специальными символами (такими как дефис, точка, запятая и т. д.). В терминах vi эти две единицы именуются «большим словом» (bigword) и «словом» (word) соответственно. Предложение – последовательность слов, ограниченная точкой или пустой строкой. Параграф – часть текста, обрамленная пустыми строками. Чтобы вы знали, что искать, просто перечислю эти команды: B, W, E, b, w, e, (, ), {, }, <Ctrl-F>, <Ctrl-B>, <Ctrl-D>, <Ctrl-U>, <Ctrl-E>, <Ctrl-Y>. Познакомиться с ними можно
№3(16), март 2004
на страницах «man vi» или методом «научного тыка». Например, команда «5<Ctrl-Y>» прокрутит экран на 5 строк вниз, не перемещая курсор (очень полезна, если вам нужно увидеть несколько строк выше и затем продолжить редактирование текущей строки). Еще несколько полезных команд навигации: ! z. – прокручивает текст так, что текущая строка становится в центре экрана; ! [№]G – перемещает курсор на №-ю строку от начала файла, если № не задано – на конец файла; ! [№]H – перемещает курсор на №-ю сверху строку, видимую на экране; ! [№]L – перемещает курсор на №-ю снизу строку, видимую на экране; ! M – перемещает курсор на строку, расположенную в центре экрана. С помощью следующих двух команд вы сможете расставлять «маркеры» в тексте, и затем быстро переходить на эти метки: ! m<симв.> – запоминает текущую позицию курсора как символ <симв.>; ! `<симв.> – возвращает курсор на позицию, запомненную как <симв.>. Например, запомнив начало второго параграфа как «m2», вы в дальнейшем сможете возвращаться к нему командой «`2».
Команды поиска С поиском вроде все понятно, ничего пояснять не буду: ! /<образец поиска> – поиск в тексте по образцу; ! / – повторный поиск по предыдущему образцу (найти далее); ! ?<образец поиска> – поиск по образцу в обратном направлении; ! ? – повтор поиска по предыдущему образцу в обратном направлении. Предыдущие четыре команды должны обязательно завершаться нажатием клавиши <Enter>. ! [№]<Ctrl-A> – поиск №-го слова, совпадающего с тем, на котором стоит курсор, начиная с позиции курсора в сторону конца документа; ! [№]f<char> – перемещает курсор на №-й после курсора символ <char> в строке; ! [№]t<char> – перемещает курсор на символ, стоящий перед №-м после курсора символом <char> в строке; ! [№]F<char> – перемещает курсор на №-й перед курсором символ <char> в строке; ! [№]T<char> – перемещает курсор на символ, стоящий после №-го перед курсором символа <char> в строке.
Команды редактирования и форматирования Помимо рассмотренных выше команд «i» и «a», полезны будут и следующие: ! [№]I – включает режим вставки текста. Текст будет вводиться с начала строки;
27
администрирование ! [№]A – включает режим вставки текста. Текст будет
! [№]c<направление> – удаляет символы в указанном
вводиться после последнего символа в текущей строке; [№]o – вставка новой строки после текущей. Текст будет вводиться с начала новой строки; [№]O – вставка новой строки выше текущей. Текст будет вводиться с начала новой строки.
направлении, которое задается командами перемещения курсора (№ отдельных символов для направлений «влево» и «вправо» и № строк для «вверх» и «вниз») и переводит редактор в режим вставки текста.
! !
Рассматривая две предыдущие команды, необходимо указать на одну особенность. Использование оных со счетчиком № не добавляет № пустых строк, как следовало бы ожидать. Добавляется одна, редактор переходит в режим ввода, а затем, после возврата в командный режим, введенный блок текста будет повторен № раз. Кстати, и команды вставки ведут себя аналогичным образом – сначала вы получаете возможность вставить или добавить текст, начиная с соответствующей позиции курсора, после чего, в момент возврата в режим команд, введенный вами текст будет продублирован № раз. Выполните команду «5i», введите текст «echo », вернитесь в режим команд (<escape>) и посмотрите, что из этого получится. Данный комментарий относится и к ряду команд, перечисленных ниже. ! [№]r<char> – заменяет символ в точке нахождения курсора (и №-1 последующих символов) символом <char>; ! [№]~ – заменяет текущий и следующие №-1 символов этими же символами в другом регистре; ! [№]s – заменяет № символов в строке, начиная с текущего, вводимым далее текстом. Вводимые символы после №-го добавляются после замененных. Граница области замены отмечается символом «$». Поясним эту команду. Пусть у нас есть строка с текстом «This is a big string». Мы хотим слово «big» заменить более справедливым «small». Поставим курсор на букву «b» (естественно, используя команду «fb» – мы же уже не маленькие, чтобы стрелочками по тексту скакать). Заменить нам нужно три символа, поэтому: «3s». Теперь просто вводим наше «small» – первые три символа введутся в режиме замены, последующие добавятся, не затирая то, что нам нужно. Теперь <escape>, и любуемся на дело наших рук, не забывая при этом громко восхищаться редактором. ! [№]R – включает режим замены (вводимые символы будут замещать текущие до конца строки, затем символы будут добавляться); ! [№]S – очищает строку и переходит в режим вставки текста (аналогичный результат достигается последовательным выполнением двух команд «[№]dd» и «O»). Обратите особое внимание, что № в данном случае относится к удалению, то есть № строк будут удалены, и вместо них можно будет ввести один блок текста. ! [№]C – удаляет символы от текущей позиции курсора до конца строки и переходит в режим вставки текста. № также относится к удалению.
28
Все вышеприведенные команды переводят vi в режим вставки текста, то есть весь последующий ввод будет отображаться на экране начиная с указанной позиции. Для возврата в командный режим используется комбинация <Ctrl-C> или клавиша <escape>. ! [№]x – удаляет № символов после курсора (начиная с позиции курсора); ! [№]X – удаляет № символов перед курсором; ! [№]d <направление> – удаляет № символов/строк относительно курсора в указанном направлении. Направление задается командами управления курсором «h», «j», «k», «l» или стрелками. Если выбрано «влево» или «вправо», то удаляются № символов соответственно перед курсором или после него. Направление, заданное как «вверх» или «вниз», позволяет удалить № строк соответственно выше или ниже текущей строки (включая текущую); ! [№]D – удаляет символы начиная с позиции курсора до конца строки; ! [№]J – объединяет текущую строку с № следующими в одну; ! [№]> <направление> – сдвигает № строк вправо на символ табуляции; ! [№]< <направление> – сдвигает № строк влево на символ табуляции. В предыдущих двух командах (впрочем, как и в остальных) направление задается командами управления курсором «h», «j», «k», «l» или стрелками. Так, если выбрано «вправо» или «влево», то команда сдвига действует только на текущую строку, № игнорируется. Если «вверх» или «вниз», то сдвигается данная строка и № предыдущих или последующих соответственно. ! [№]#{#|+|-} – инкремент/декремент числа. Если дать эту команду, когда курсор стоит под числом, то это число будет увеличено на № (если задан параметр «#» или «+») или уменьшено на № (если задан параметр «-»). Например, чтобы увеличить число 52 на 7, ставим курсор на символ «5» или «2» (он может быть установлен на любую цифру числа), после чего набираем последовательно «7#+». В результате «52» изменится на «59». В vim эта функция не поддерживается. ! [№]!<направление><команда shell> – заменяет в документе начиная с позиции курсора в заданном направлении № строк выводом команды оболочки. Если вам нужно вставить вывод, можно сначала создать две пустые строки, а затем выполнить данную команду, например: «2o», <escape>, «!kwho».
Работа с блоками и буфером Vi предоставляет пользователю весьма мощные функции по работе с блоками.
администрирование ! [№]yy – копирует в буфер № строк, начиная с текущей; ! [№]Y – копирует № строк в буфер (аналогична «yy»); ! [№]y<направление> – копирует текст в буфер в ука-
! !
! <Ctrl-Z> – временное прерывание сессии редактирования и выход в командную оболочку. Чтобы вернуться назад в редактор, нужно воспользоваться системной командой «fg», которая переводит фоновый процесс в активный. Номер задачи (job), соответствующий отложенному процессу редактирования, который потребуется ввести как параметр команды «fg», можно уточнить командой «jobs». При попытке выйти из оболочки командой «exit» вы получите предупреждение, что у вас остались незавершенные задачи:
занном направлении. № трактуется в зависимости от направления (впрочем, как и в остальных подобных командах): количество копируемых символов для «вправо» и «влево» и число строк (дополнительно к текущей) для «вверх» и «вниз». Например, «2yk» скопирует в буфер текущую строку и еще две, расположенные выше (итого – три). Если вам нужно скопировать только текущую строку, следует использовать команду «yy» или «Y»; [№]p – вставляет текст из буфера № раз после курсора; [№]P – вставляет текст из буфера № раз перед курсором.
$ exit You have stopped jobs. $ jobs [1] 34336 Suspended $ fg 34336
Используя команды расширенного (ex) режима, можно работать с несколькими блоками. Где изучить эти возможности, вы, думаю, уже знаете. Конечно же, в «man vi».
Команды режима редактирования Находясь в режиме редактирования, редактор все вводимые символы будет отображать на экране как часть текста. Однако существует несколько последовательностей, которые трактуются как специальные команды и для которых выполняется автоматическая замена введенных символов последовательности результатом выполнения команды. Наиболее полезные из них следующие: ! <escape> – завершает режим ввода текста и переводит редактор в командный режим; ! <Ctrl-C> – также возвращает редактор в режим команд (при «слепой» печати эта команда более удобна); ! <backspace> – удаление только что введенного символа; ! <Ctrl-W> – удаление только что введенного слова; ! <Ctrl-X>[0-9A-Fa-f] – вставка символа, имеющего код, выраженный шестнадцатеричным числом (например, «<Ctrl-X>30» отобразится на экране как «^X30» и автоматически заменится символом «0», ASCII-код которого – 30h).
Прочие команды режима visual Осталось рассмотреть еще несколько команд, которые трудно выделить в ту или иную категорию, но которые весьма полезны при работе. ! [№]. – повтор последней команды для текущей позиции курсора № раз; ! u – отменяет последнее действие; ! U – восстанавливает текущую строку, отменяя все изменения, сделанные в ней; ! Q – переключение в интерфейс ex (см. далее); ! <Ctrl-G> – выводит информацию о редактируемом файле (состояние – modified/unmodified, отражающее, есть ли в файле несохраненные изменения; номер текущей строки и общее количество строк в файле); ! <Ctrl-R> – перерисовывает экран (может пригодиться на плохом канале связи или для восстановления экрана после вывода на него системных сообщений, если вы работаете с физической консоли);
№3(16), март 2004
!
vi test
Здесь test – имя редактируемого файла. Нужно заметить, что если вы вызываете vi из Midnight Commander, то в списке приостановленных заданий вместо «vi test» будет отображаться «midc» («mc» для Linux). После этих действий вы вернетесь назад в vi и сможете завершить редактирование. Если отложенное задание только одно, можно вернуться к нему командой fg без параметров. ZZ – выход из редактора с сохранением изменений (аналог команды «:wq»).
Команды режима ex Редактор vi имеет помимо рассмотренного еще один интерфейс, или режим, – ex. Запустить редактор в этом режиме можно одной из следующих команд: # vi –e # ex
Кроме того, вы можете перейти в него из режима visual по команде «Q». Данный интерфейс предоставляет ряд расширенных возможностей для обработки текста, таких как работа с несколькими буферами, открытие других файлов в текущем сеансе, изменение настроек редактора и т. д. В данном разделе будут рассмотрены лишь наиболее полезные из них. В отличие от интерфейса vi, ex ориентирован на командную работу со строками. Редактирование идет как бы вслепую. Редактируемый текст на экране по умолчанию не отображается, вам придется специально вызывать на экран требуемые строки. Почти все ex-команды можно исполнить из vi-интерфейса, предварив команду двоеточием. Наиболее характерный пример: команда – выход из редактора: «:q». Ниже приведено лишь несколько полезных команд: ! :!<команда> – выполняет команду оболочки (результат просто выводится на экран); ! :[<start>] # [№] – выводит на экран № строк начиная со <start> (в режиме ex); ! :s/<текст1>/<текст2> – замена в текущей строке <текст1> на <текст2>; ! :s – повтор предыдущей замены для текущей строки; ! :%s/<текст1>/<текст2> – замена во всем файле <текст1> на <текст2>; ! :next – переход к редактированию следующего файла из списка параметров, с которым был вызван редактор. ! :prev – возврат к редактированию предыдущего файла.
29
администрирование Например, если вы вызываете редактор командой «vi file1 file2», то в процессе редактирования вы сможете переходить между файлами указанными выше командами. При этом содержимое буферов обмена будет сохраняться, и вы сможете вставлять в файл фрагменты, скопированные из другого файла. Заметьте, что редактор не позволит вам перейти к другому файлу, пока изменения в текущем не будут сохранены или отменены (отменить все изменения можно командой «:e!», которая перечитает редактируемый файл с диска). ! :edit <file> – открыть для редактирования файл <file>; ! :w[ <filename>] – уже знакомая вам команда сохранения редактируемого файла. Опциональный параметр <filename> превращает команду в команду «Сохранить как…» для записи изменений в файл с другим именем. Еще одно пояснение – очень часто бывает, что, отредактировав тот или иной файл (например, squid.conf), при попытке сохранить сделанные изменения вы получаете сообщение, что файл недоступен для записи. Причина понятна – или вы забыли войти как root, или файл имеет права «r--r--r--». Выходить без сохранения, менять права и редактировать снова – жалко… А вот сохранить файл под другим именем, а затем, обретя требуемые права, заменить им оригинал – как раз то решение, которое нас устраивает. Конечно, можно с другого терминала поменять права на сохраняемый файл и снова провести операцию записи. Но при удаленной работе открыть новую ssh-сессию не всегда проще, чем поступить описанным выше образом. Да и удаленный доступ к нескольким терминалам иногда запрещают из соображений безопасности. Кроме того, если вы внесли изменения в системный файл (скажем, /etc/crontab), будучи зарегистрированным как простой пользователь, то вряд ли безопасно менять к нему права доступа, пусть и кратковременно. Кроме того, статья рассматривает редактор vi, а потому примеры призваны прежде всего показать его пригодность для решения тех или иных задач, пусть и не всегда оптимальным образом. ! :g /<шаблон>/ <команда> – указанная команда, которой может быть одна из ex-команд, применяется к строкам, которые соответствуют шаблону. Например, команда «:g /qwerty/ delete» удалит все строки, в которых присутствует последовательность символов «qwerty»; ! :v /<шаблон>/ <команда> – работает аналогично предыдущей, но команда применяется к тем строкам, которые не соответствуют шаблону; ! :vi – переход в интерфейс visual.
Опции и параметры редактора Было бы странно, если бы оказалось, что столь мощный редактор не позволяет настраивать себя под требования и предпочтения конкретного пользователя. Однако не следует думать, что настройкой можно превратить vi в редактор ee или что-то подобное. Следующие опции позволяют лишь добавить «удобства», не меняя общих принципов работы. Все они устанавливаются с помощью ex-команды «:set». «no» перед именем опции отключает ее.
30
! :set [no]list – включает режим отображения служебных ! ! !
символов (таких как конец строки, который будет отображаться символом «$», и т. д.); :set [no]nu – включает отображение номеров строк; :set [no]showmode – включает отображение режима (command, insert, append, replace) в нижнем правом углу экрана, в котором редактор находится в данный момент; :set [no]verbose – включает режим расширенных сообщений (на каждое ошибочное действие будет выдаваться соответствующее пояснение).
За остальными настройками, как всегда, – «man vi», соответствующий раздел. Увековечить сделанные настройки можно в exrc-файлах. Данные файлы применяются в следующей последовательности: ! /etc/vi.exrc – глобальный файл настроек редактора; ! $HOME/.exrc – пользовательский файл настроек; ! .exrc – локальный файл настроек для текущей директории. Действовать будут те настройки, которые применены последними. В эти файлы следует занести те ex-команды, которые должны быть исполнены при открытии редактора. Например, если вы хотите, чтобы редактор всегда работал с включенным отображением режима и расширенными сообщениями, то создайте в своем домашнем каталоге файл .exrc следующего содержания: set showmode set verbose
Теперь каждый раз при вызове редактора эти команды будут отрабатываться автоматически.
Заключение Итак, вы в общих чертах познакомились с редактором vi. Приведу еще несколько преимуществ редактора vi, помимо его функциональности: ! способность работать практически на любых терминалах; ! обязательное наличие в любой UNIX-системе; ! надежная работа даже на самых плохих линиях; ! используется в некоторых системных командах (например, vipw), следовательно, умение в нем работать потребуется в любом случае; ! а сможете ли вы просмотреть файл, являющийся каталогом (в UNIX каталог – это специальный файл), с помощью «ee» или «mcedi»? Хотя редактировать такой файл вам и vi не позволит, но посмотреть для интереса – можно (например, «view /etc»). Попытайтесь с ним подружиться! P.S. Если вы все же отважились сделать vi вашим основным редактором, для этого установите значение переменной $EDITOR=vi (например, для оболочки sh нужно поправить файл .profile в вашем домашнем каталоге, для csh – это файл .cshrc). Также в настройках Midnight Commander отмените использование встроенного редактора (пункт меню «Настройки» – «Конфигурация…»), и по <F4> будет вызываться тот редактор, который задан в переменной $EDITOR.
безопасность
СВОБОДНЫЙ АНТИВИРУС До недавнего времени об установке антивируса под UNIX-системы никто сильно, пожалуй, и не думал, но события последних лет изменили коренным образом подход к этому вопросу. Теперь антивирус под эти системы ставят не только для обезвреживания вирусов при использовании UNIX-систем в качестве платформы для почтовых и файловых серверов локальных сетей и т. д., но и для локальной защиты пользовательских данных от деструктивных действий вирусов. При этом антивирусы должны обладать возможностью обнаруживать все существующие на данный момент вирусы как для UNIX, так и для Windows-систем, а также макровирусы.
СЕРГЕЙ ЯРЕМЧУК
32
безопасность Наибольшей популярностью среди антивирусов пользуется DrWeb от ЗАО «ДиалогНаука»: http://www.drweb.ru, обладающий действительно хорошими характеристиками и эвристическим анализатором, позволяющим иногда обнаружить неизвестный вирус. Все, в общем, хорошо, но, собрав полностью сервер из бесплатных компонентов, выбить финансы для того, чтобы платить за лиценцию, под конец года не получилось. А ограничения ознакомительной версии, в частности невозможность проверки архивов, мне не совсем подходят. Плюс зацикливание на одном продукте обычно мешает увидеть его слабые и сильные стороны, все, как говорится, познается в сравнении. Тем более что в Интернете я постоянно натыкался на другие антивирусы, и мне захотелось поискать замену (или убедиться в отсутствии таковой, что тоже хорошо). В результате я вышел на несколько довольно интересных проектов, о которых речь пойдет в этой и следующей статьях. Clam AntiVirus (http://clamav.sourceforge.net/ или http:// www.clamav.net/) представляет собой антивирусный комплект для UNIX-систем. Главная цель продукта – интеграция с почтовыми серверами для проверки вложений на предмет наличия вирусов. В настоящий момент поддерживается широкий спектр операционных систем: Linux, Solaris, FreeBSD, OpenBSD, NetBSD, AIX, Mac OS X, BeOS, HPUX, SCO UNIX и Windows/Cygwin на нескольких архитектурах: Intel, Alpha, Sparc, Cobalt MIPS boxes, PowerPC, RISC 6000, что уже вызывает уважение. Распространяется по лицензии GPL, POSIX-интерфейс и общедоступные библиотеки позволяют быстро адаптировать его с другими приложениями. Работает с архивами и сжатыми файлами, в настоящее время встроена поддержка RAR, Zip, Gzip, Bzip2. Также встроена поддержка защиты от mail-бомб, которые периодически любят закидывать в пользовательские ящики, и поддерживается milter-интерфейс к программе Sendmail. Обнаруживает, по данным разработчиков, более 20 000 вирусов, червей и троянов (хотя программа при запуске выдает сообщение о чуть больше 10 000, остальное в дополнительных базах). Конечно, по сравнению с другими подобными продуктами число получилось небольшое, но ведь мы знаем, что количество можно считать по-разному, хотя сама программа при запуске выдает сообщение о 10 000 вирусов. При необходимости можно воспользоваться он-лайн-сканером, позволяющим протестировать файлы на жестком диске. Для этого заходим по адресу: http://www.gietl.com/test-clamav и указываем на файл. Если же вы имеете вирус, который не обнаруживается ClamAV с обновленными базами, то по адресу: http:// www.nervous.it/~nervous/cgi-bin/sendvirus.cgi или http:// www.clamav.net/cgi-bin/sendvirus.cgi можно заполнить форму, где нужно обязательно указать свой e-mail, имя и место расположения зараженного файла, опционально можно также проставить антивирус, обнаруживший заразу. Другой вариант: послать zip-архив с паролем virus по адресу virus@clamav.net. После чего вирус будет проанализирован и добавлен в базу данных. Об остальных возможностях поговорим, когда посмотрим на его работу в действии.
№3(16), март 2004
Установка Из библиотек требуются zlib и bzip2, которые имеются в большинстве систем. Если привычнее пользоваться уже скомпилированными пакетами, то зайдите на страницу http:/ /clamav.sourceforge.net/binary.html и выберите нужную ссылку: на момент написания статьи это Debian, RedHat – Fedora, PLD (Polish(ed) Linux Distribution), Mandrake, AIX, FreeBSD, OpenBSD и MS Windows, на установке в этом случае останавливаться не буду. В остальных случаях нужно компилировать самому, хотя ничего особо сложного разработчики не придумали. #tar xzvf clamav-0.65.tar.gz # cd clamav-0.65
Единcтвенная задержка возникает при конфигурировании и выглядит это так: #./configure ... /dev/(u)random detected. Checking /etc/passwd... ERROR: User "clamav" (and/or group "clamav") doesn't exist. Please create it. You can omit this check with the --disableclamav option.
Дело в том, что разработчики в целях безопасности рекомендуют запускать утилиту от лица пользователя clamav, а не root, и не устанавливать ни в коем случае SUID или SGID. Избежать этого сообщения можно двумя способами: добавить параметр --disable-clamav при конфигурировании или просто создать этого пользователя. # groupadd clamav # useradd -g clamav -s /bin/false -c "Clam AntiVirus" clamav
По умолчанию ClamAV устанавливается в /usr/local, и, соответственно, конфигурационный файл будет лежать в /usr/local/etc, его месторасположение на /etc можно изменить, добавив опцию --sysconfdir=/etc. Если конфигурирование завершилось без ошибок, компилируем и устанавливаем. # make # su -c "make install"
После окончания установки в нашем распоряжении будет несколько исполняемых файлов: ! clamscan – утилита командной строки, предназначенная для проверки файлов и каталогов на предмет наличия вирусов. ! clamd – антивирусный демон, прослушивающий подключения к UNIX или TCP-сокетам и сканирующий каталоги по требованию. Обеспечена возможность onaccess просмотра (только Linux) при применении утилиты clamuko. ! clamdscan – простой интерфейс к демону clamd, позволяет также сканировать файлы и каталоги, при этом используются те же параметры, что и в clamscan, и может полностью заменить clamscan. ! clamav-milter (при конфигурировании с опцией --enablemilter) – представляет собой антивирусный интерфейс к sendmail, использует для просмотра почты clamd.
33
безопасность ! freshclam – утилита автоматического обновления ви!
русной базы данных через Интернет, позволяющая держать ее в самом современном состоянии. sigtool – генерирует вирусную сигнатуру, используя внешний антивирусный сканер, который способен обнаружить вирус. Может создавать шестнадцатиричный дамп и формировать и распаковывать CVD-базу данных (ClamAV Virus Database).
Теперь по порядку и поподробней. Проверить текущий каталог на наличие вирусов можно, просто набрав clamscan без каких-либо параметров. В результате получим список проверенных файлов и отчет. Список просканированных файлов позволяет проверить работу утилиты с различными типами файлов на начальном этапе, но в большинстве случаев лучше добавить параметр -i для вывода только зараженных файлов. Указать на файлы, находящиеся в другом каталоге, можно, перечислив их в строке запуска, или, если проверяется каталог, то указать путь к нему, не забыв опцию -r для рекурсивного обхода (для проверки работы антивируса с пакетом поставляется несколько тестовых файлов). # /usr/local/bin/clamscan -r -i /home/sergej/work/clamav-0.65/
паковки, и дополнительных указаний ей не надо, но если появляются сообщения вроде:
то указываем на необходимость проверки архивов, и если не видно нужного архиватора в переменной $PATH, то указываем также местонахождение такой программы. Например, для rar: -unrar[=FULLPATH]. Так, для zip-архива строка запуска может выглядеть так: # clamscan –unzip /mnt/test/test.zip
После чего программа должна вывести список всех файлов архива с результатами проверки. И еще одна проблема может подстерегать при проверке архивов. Выглядит она так.
Каталог /tmp забивается таким образом очень быстро, т.е., закинув большой архив, можно провести DOSатаку. Чтобы избежать этого, можно указать при помощи -tempdir= на другой каталог, в котором побольше свободного места, или установив максимальное количество извлекаемых за один раз файлов (-max-files=#n), или извлечь сначала #n Кб архива (использовав nM или nm, можно указать на количество Мб) при помощи –max-space=#n. Можно просто указать на максимальный уровень рекурсии обхода архива: -max-recursion=#n. В следующем примере используем новую антивирусную базу и ограничиваем размер временных файлов в 50 Мб, плюс проверяем архивы. #clamscan -d /tmp/newclamdb –tgz –deb –unrar ↵ --max-space=50m -r /home
Опция --database= позволяет указать место расположения дополнительной антивирусной базы. Также по умолчанию пограмма не ведет никаких логов, при работе с cron это не совсем удобно, т.к. теряется контроль над работой программы, при помощи --log= можно указать, в какой файл их заносить. При необходимости проверки отдельных файлов каталога можно воспользоваться опциями exclude=PATT и -include=PATT. Первая позволяет указать шаблоны файлов, которые не надо проверять, а вторая, наоборот, только те, которые надо просканировать в поиске вирусов. Опция -mbox включает сканирование почтовых каталогов и файлов. #clamscan -r --mbox /var/spool/mail
Или можно проверять выход другой программы на наличие вирусов.
Я надеюсь, по clamscan все понятно. Переходим к демону clamd. Главное отличие демона от сканера заключается в том, что он один раз при старте загружает все необходимые базы и настройки и находится в оперативной памяти постоянно готовым выполнить работу. В своей работе он использует конфигурационный файл clamav.conf. Если запустить программу без дополнительного редактирования (в том случае, если установка происходила из исходных текстов), то демон откажется работать. # /usr/local/sbin/clamd
Файл хорошо комментирован, и опции описаны в man, чтобы заставить работать демон в конфигурации по умолчанию, достаточно убрать или закомментировать строку Example в самом начале файла. Пример (ненужные параметры достаточно закомментировать):
#cat testfile | clamscan -
Кроме вывода информации об обнаружении вируса можно удалить (--remove) или переместить (--move=DIRECTORY) такие файлы в другой каталог. Обычно при работе с архивами программа сама находит нужную утилиту для рас-
34
#Example # Ïóòü ê ëîã-ôàéëó LogFile /var/log/clamav/clamd.log # Áëîêèðîâêà çàïèñè â ëîã-ôàéë (íåîáõîäèìà ïðè çàïóñêå # íåñêîëüêèõ äåìîíîâ îäíîâðåìåííî)â òîì ÷èñëå è âî èçáåæàíèå # ðàáîòû ñ îäèíàêîâîé êîíôèãóðàöèåé #LogFileUnlock
безопасность # ìàêñèìàëüíûé ðàçìåð ëîã-ôàéëà (0 – áåç îãðàíè÷åíèé) LogFileMaxSize 0 # Èñïîëüçîâàíèå syslog LogSyslog # Ïîäðîáíûé îò÷åò #LogVerbose # Ôàéë äëÿ ñîõðàíåíèÿ èäåíòèôèêàòîðà ïðîöåññà PidFile /var/run/clamav/clamd.pid # Ïóòü ê àíòèâèðóñíûì áàçàì (ïî óìîë÷àíèþ /usr/local/share/clamav) DataDirectory /var/lib/clamav # Äåìîí ìîæåò ðàáîòàòü â ñåòåâîì èëè ëîêàëüíîì ðåæèìå, # â öåëÿõ áåçîïàñíîñòè ðåêîìåíäóåòñÿ ïîêà ïîñëåäíèé, íî âîò # ïîñûëàòü ñèãíàëû ìíå ïîêàçàëîñü áîëåå óäîáíûì èìåííî # â ñåòåâîì. Íåñêîëüêî ñëåäóþùèõ ñòðîê íåîáõîäèìû äëÿ # íàñòðîéêè ñåòåâîãî ðåæèìà. #LocalSocket /var/run/clamav/clamd.sock #FixStaleSocket #TCPSocket 3310 #TCPAddr 127.0.0.1 #MaxConnectionQueueLength 30 # Ïðåäâàðèòåëüíàÿ çàïèñü ïîòîêà StreamSaveToDisk # Ïðåäåë äëÿ ïîòîêà, ïîñëå êîòîðîãî ñîåäèíåíèå çàêðûâàåòñÿ #StreamMaxLength 10M # Ìàêñèìàëüíîå êîëè÷åñòâî îäíîâðåìåííî âûïîëíÿåìûõ çàäà÷ MaxThreads 10 # Ìàêñèìàëüíàÿ ðåêóðñèÿ êàòàëîãà MaxDirectoryRecursion 15 # Ñëåäîâàíèå ñèìâîëè÷åñêèì ññûëêàì äëÿ êàòàëîãîâ è ôàéëîâ #FollowDirectorySymlinks #FollowFileSymlinks # Ïðîâåðêà öåëîñòíîñòè áàç (ïî óìîë÷àíèþ 1 ÷àñ) #SelfCheck 600 # Êîìàíäà, êîòîðàÿ äîëæíà âûïîëíèòüñÿ ïðè îáíàðóæåíèè âèðóñà. # Ïðè ýòîì èñïîëüçóþòñÿ ïîäñòàíîâêè %f – èìÿ èíôèöèðîâàííîãî # ôàéëà, %v – íàçâàíèå âèðóñà. Äîëæåí èñïîëüçîâàòüñÿ ïîëíûé # ïóòü ê êîìàíäå #VirusEvent /usr/local/bin/send_sms 123456789 "VIRUS ALERT: %f: %v" # Èìÿ ïîëüçîâàòåëÿ, îò êîòîðîãî çàïóñêàåòñÿ äåìîí, îí äîëæåí # èìåòü ïðàâà íà èçìåíåíèå âñåõ ïåðå÷èñëåííûõ ôàéëîâ è êàòàëîãîâ. User clamav # Ðàáîòà ñ ïî÷òîé è àðõèâàìè, äëÿ RAR íóæíà îòäåëüíàÿ ñòðîêà ScanMail ScanArchive ScanRAR # Óñòàíîâêà ìàêñèìàëüíûõ çíà÷åíèé äëÿ àðõèâîâ äëÿ çàùèòû # îò mail-áîìá (0 – áåç îãðàíè÷åíèé). ArchiveMaxFileSize 10M ArchiveMaxRecursion 5 ArchiveMaxFiles 1000 ArchiveLimitMemoryUsage
И далее в файле вы найдете несколько строк для работы с Clamuko. Это интерфейс к модулю Dazuko (http:// dazuko.org), обеспечивает (только для Linux) работу clamd в режиме on-access через устройство /dev/dazuko. Это пока еще экспериментальная разработка, пока я не убедился в необходимости пользоваться ею, поэтому, если кто-то заинтересовался, за подробностями обращайтесь в документацию, в ней все понятно расписано. Запущенный демон ничего не делает. Для указания того, чем именно сейчас ему заниматься, необходимо послать сигнал: ! PING – проверка связи, в ответ демон посылает PONG, и закрывает соединение. ! VERSION – вывод версии. ! RELOAD – перезагрузка антивирусных баз. ! QUIT – остановка демона. ! SCAN file/directory – рекурсивный обход указанных каталогов в поисках вирусов с поддержкой архивов (если не запрещено в конфигурационном файле). ! RAWSCAN file/directory – то же, только без поддержки работы с архивами. ! CONTSCAN file/directory – то же, что и SCAN, но при обнаружении вируса программа не прерывает свою работу, а продолжает обход каталога дальше.
№3(16), март 2004
! STREAM – просмотр потока, при этом демон выдаст номер порта, в который необходимо посылать сигнал. С сигналами понятно, только вот как их посылать, в документации сказано довольно невнятно. Оказалось, все замешано на межпроцессорном взаимодействии или подключении к сетевому порту. Например, команду «просканировать каталог» можно дать таким образом (при сетевом режиме работы демона): # telnet localhost 3310
Поэтому все-таки более удобным способом является использование утилиты clamdscan, просто введя в качестве аргумента проверяемый каталог или файл. #clamdscan /home
Для автоматического запуска clamd вместе с системой необходимо положить файл clamd.sh в каталог /etc/init.d/ и прописать путь для запуска в /etc/rc.d/rc.local или создать символическую ссылку в каталоге соответствующему уровню запуска системы. #ln -s /etc/init.d/clamd.sh /etc/rc.d/rc5.d/S50clamd
Хотя все в этом вопросе зависит от используемой операционной системы или дистрибутива Linux. С clamav-milter все просто. Если антивирус устанавливался при помощи rpm-пакетов, то необходимо доустановить пакет clamav-milter-0.65-4.i386.rpm, в котором практически все уже настроено, и в каталоге /usr/share/doc/ clamav-milter-0.65/ лежат документы RPM-clamav-milter.txt и HOWTO-logwathch.txt, в которых все расписано по шагам. При установке из исходников также ничего сложного. Добавляем в файл /etc/mail/sendmail.mc строку: INPUT_MAIL_FILTER(`clmilter',`S=local:/var/run/clmilter.sock, F=, T=S:4m;R:4m')dnl define(`confINPUT_MAIL_FILTERS', `clmilter') À â clamav.conf ïðîâåðÿåì íàëè÷èå òàêèõ ñòðîê. LocalSocket /var/run/clamd.sock ScanMail StreamSaveToDisk
И запускаем утилиту: #/usr/local/sbin/clamav-milter -blo /var/run/clmilter.sock
При необходимости ограничить число процессов добавляем опцию -max-children=, если сlamd работает в сетевом режиме, то дополнительно используется опция -server= с указанием IP-адреса, последним аргументом в этом случае проставляется номер порта.
35
безопасность Утилита автоматического обновления антивирусных баз может запускаться в двух режимах: интерактивном – из командной строки, и как демон. Утилита использует базу http://database.clamav.net для автоматического выбора зеркала. В комплекте имеется также список таких баз, занесенный в файл mirror.txt, утилита пробует по порядку соединиться с первым в списке и в случае неудачи далее следует по списку. Можно подобрать для себя оптимальный вариант и поставить его первым. Для начала следует запустить утилиту без параметров, если все нормально, то следующим создать лог-файлы, необходимые для работы.
морфного варианта. В документе «Creating signatures for ClamAV», который поставляется вместе с архивом, расписано, как получить сигнатуру из тестовых «вирусов», поставляемых вместе в ClamAV. Мне повезло чуть больше. Погоняв чуть дольше недели антивирус в боевом режиме, удалось найти вирус который ClamAV не обнаружил, а Dr.Web по его поводу сказал следующее: #drweb
/mnt/dos.ext.1/virus_test/
# touch /var/log/clam-update.log # chmod 600 /var/log/clam-update.log # chown clamav /var/log/clam-update.log
Для запуска в режиме демона используется опция -d: # freshclam -d -c 3 -l /var/log/clam-update.log
Параметр -с указывает на промежуток времени обновления базы в днях (число от 1 до 50). Для указания отличной от установленной по умолчанию директории, в которую должны помещаться обновления, используйте опцию -datadir=. Прокси можно указать двумя способами: либо задать в командной строке параметры -httpproxy=hostname[:port] и при необходимости указания пароля для доступа -proxy-user=user:password; второй вариант – установить нужное значение переменной http_proxy: #export http_proxy="proxy.server:8080"
Для контроля за обновлениями можно воспользоваться параметрами: -on-error-execute=COMMAND и -onupdate-execute=COMMAND. Первая позволяет задать команду, которая будет выполнена при неудачном обновлении баз, вторая – наоборот, при успешном проведении обновления. Для одноразового запуска утилиты в случае необходимости немедленного обновления она запускается без опции -d (да и -с смысла не имеет). Остальные параметры при этом остаются теми же. В rpm-пакете лежит файл-шаблон /etc/sysconfig/freshclam, в котором можно заполнить соответствующие поля для установки всех вышеперечисленных параметров, а запускать утилиту при помощи скрипта /etc/init.d/freshclam. Можно для запуска использовать и cron, занеся в /etc/сrontab для еженедельного обновления примерно такую строку:
Другой антивирус F-prot, о котором речь пойдет в следующий раз, выдал сообщение:
Очевидно, разработчики ориентируются в первую очередь на новые, недавно появившееся вирусы, и антивирус пока плохо знаком со старой гвардией. Сначала пробуем создать такую сигнатуру при помощи clamscan: # sigtool -c "clamscan --stdout" ↵ -f /home/sergej/virus_test/test.exe -s
Опция -с говорит, какую команду запускать, здесь должен быть один из установленных в системе антивирусов, -f задает инфицированный файл, а -s – уникальная строка, которую вывел антивирус, обнаруживший вирус, в большинстве случаев сюда пишем имя обнаруженного вируса. Как видите, при помощи clamscan обнаружить его не получилось, поэтому пробуем Dr.Web. # sigtool -c "drweb" -f -f /home/sergej/virus_test/test.exe ↵ -s "Win32.HLLP.Underscore.36864"
0 00 * * 07 /usr/local/bin/freshclam --quiet ↵ -l /var/log/clam-update.log
И последняя, довольно интересная утилита sigtool, позволяющая самим создать готовую сигнатуру для добавления в свою антивирусную базу. Таким образом можно попробовать самим создать себе антидот во время очередной эпидемии до реакции компаний, выпускающих антивирусное ПО. Естеcтвенно, это все-таки полумера, т.к. автоматически без глубокого анализа будет довольно трудно так сразу создать сигнатуру, действующую на все варианты обнаруженого вируса, особенно в случае поли-
36
То есть сигнатура получилась больше, чем рекомендуемые 40... 200 символов. В этом случае в документе показано, как вручную найти нужную сигнатуру, но для этого как минимум необходимо немного знать что-то о виру-
безопасность се. Этот вирус, например, переименовывает файлы и создает в системном каталоге файл mc42.exe. Поэтому при помощи Midnight Commander, или дав такую команду: # strings test.exe | less
или двоичный дамп для анализа: #cat
test.exe
| sigtool --hex-dump > virus.sig
а лучше, воспользовавшись двоичным редактором, находим специфические для данного вируса строки (рис. 1) и заносим их в отдельный файл. Но этот метод хоть интересен и работает на ура, мне созданная таким образом сигнатура позволила найти все файлы на зараженном компакте, в том числе и упрятанные в архив (куда не смог заглянуть Dr.Web), но все-таки этот процесс может занять значительный промежуток времени, особенно при большом исходном файле. Можно попробовать разбить исходный файл на меньшие по размеру, в котором(ых) внешний антивирус будет еще находить вирус, и затем повторить операцию. Разбить файл на части можно, воспользовавшись, например, split. Мне удалось, немного повозившись, создать такой файл в 10 Кб и в результате: # sigtool -c "drweb" -f -f /home/sergej/work/xaf ↵ -s "Win32.HLLP.Underscore.36864"
В результате в файле xaf.sig будет примерно такая строка.
Добавляем в ее начало: Win32.HLLP.Underscore.36864 (Clam)=
компании не очень любят делиться своими наработками, поэтому как-то приделать внешние базы пока не получается, но некоторые сигнатуры в удобочитаемом виде можно взять, например, на Sophos http://www.us.sophos.com/ downloads/ide/, по крайней мере Bagle появился там быстро, а простота операции позволила также оперативно занести информацию о нем в свою базу. В документации показано, как можно затем обратно упаковать базу, воспользовавшись опцией --build, но для этого необходимо иметь доступ к специальному серверу, для подписи, поэтому дальше я не пошел, оставил все как есть.
Ðèñóíîê 1
Предвидя некоторую критику, сразу отвечу: да, не дело сисадмина собирать по всему свету сигнатуры. Но, с другой стороны, так можно среагировать все-таки побыстрее, чем антивирусные компании (ну, по крайней мере, такая возможность греет душу), плюс новые сигнатуры добавляются в базу данных ClamAV ежедневно, поэтому, я думаю, эта проблема будет решена в скором времени. Мне этот антивирус в общем-то понравился и не в последнюю очередь разнообразием инструментов и удобством работы. Также одним из положительных моментов знакомства с ним отмечаю именно открытость продукта, позволившую наконец разобраться с технологией и понять реальный механизм работы антивирусов. Для сомневающихся в следующих статьях продолжим поиск. Успехов!
Теперь осталось добавить сигнатуру вируса в базу данных. Распаковываем одну из установленных баз, их имеется две – main.cvd и daily.cvd. Первая – постоянная, вторая – для ежедневных обновлений. Распаковываем нужную: #sigtool --unpack-current daily.cvd
после чего в текущем каталоге появится файл viruses.db2, если его открыть в любимом редакторе, то увидим, что он состоит из подобных строк. Добавляем в него сигнатуру и просчитываем новую контрольную сумму: #cat xaf.sig >> viruses.db2 #md5sum viruses.db2 > viruses.md5
Теперь можно пользоваться обновленной базой, указав на нее параметром -d или поместив обратно в каталог /var/lib/clamav или /usr/local/share/clamav/ к старым базам, после чего добавленный таким образом вирус будет обнаруживаться ClamAV. К сожалению, антивирусные
№3(16), март 2004
37
безопасность
DEVICELOCK
АНДРЕЙ БЕШКОВ В последнее время все больше людей, работающих в сфере ИТ, задумываются о безопасности. Все же недавние вирусные эпидемии и постоянные взломы тех или иных сайтов поневоле привлекают к себе внимание. В связи с этим очень четко прослеживается тенденция роста рынка услуг, тем или иным образом связанных с безопасностью. Больше всего бросается в глаза разнообразие видов брандмауэров и антивирусных систем. Практически каждое печатное и электронное издание, причастное к ИТ-тематике, посчитало необходимым рассказать либо о первом, либо о втором виде продуктов. Благодаря такому мощному потоку информации методы борьбы с внешними злоумышленниками всеми нами заучены практически наизусть. Любой мало-мальски сведущий в этом вопросе человек расскажет, что для защиты внутренней сети лучше всего вынести сервера, предоставляющие публичные сервисы, в демилитаризованные сети. А на всех шлюзах в корпоративную сеть и Интернет в зависимости от денежных средств, выделенных нам для этих целей, нужно обязательно поставить брандмауэр аппаратной или программной реализации. Ну а с вирусами мы будем бороться, проверяя, где возможно, потоки данных с помощью антивирусного программного обеспечения. Кроме описанных выше, существует еще некоторое количество добавочных мероприятий, позво-
38
ляющих повысить степень безопасности наших систем. У каждого из них свои проценты полезности и соотношение между ценой и качеством. За всем этим валом правдивой, иногда не очень, а бывает, и откровенно рекламной информации скрывается один интересный факт – большинство описываемых средств предназначены для защиты от внешних злоумышленников. На первый взгляд это естественно, но если подойти к вопросу без излишней спешки и проанализировать доступные факты, то получится, что, в соответствии с последней статистикой, восемьдесят процентов инцидентов происходят по вине людей, находящихся внутри охраняемой сети, и только двадцать процентов приходится на внешние угрозы. К сожалению, с внутренними угрозами безопасности все не так просто. Достаточно часто складывается такое положение дел, что сотрудники организации по умолчанию получают слишком большие полномочия, которые совершенно излишни для выполнения их ежедневных обязанностей. Таким образом, получается, что внутри объекта, тщательно защищаемого снаружи, образуется никем не контролируемый источник проблем. Можно защитить сервера, находящиеся внутри корпоративной сети, с помощью внутренних брандмауэров, но их использование приводит к дополнительным накладным расходам. Но все
безопасность равно у обычного пользователя остается довольно большой простор для злонамеренных действий, направленных на локальную систему и системы, находящиеся рядом. Некоторые особо продвинутые реализации брандмауэров могут менять свою схему действий в зависимости от даты и времени. Такой подход, к примеру, помогает реализовать политики, при которых определенным IP-адресам разрешено работать с теми или иными сервисами только по будним дням с 9 часов утра до 17 часов вечера. Основная проблема подобного решения заключается в том, что на одном и том же компьютере могут работать одновременно или поочередно несколько пользователей, и большинство брандмауэров не обучены принимать в расчет этот факт. Так что не стоит считать такой подход панацеей, спасающей от внутренних злоумышленников. Сегодня я хотел бы рассказать о программе DeviceLock, созданной компанией SmartLine. Эта программа помогает решить часть проблем с безопасностью рабочих мест и, таким образом, повысить общую защищенность сети изнутри. К сожалению, многие пользователи имеют привычку приносить на свое рабочее место программы и данные из посторонних источников. В этом случае никто не может поручиться за отсутствие в них троянских программ и вирусов. Самым простым способом, конечно, было бы изъять все потенциально опасные устройства. Но я думаю, что руководство не одобрит тотального демонтирования CD-ROM, DVD, дисководов и прочих устройств, способных переносить данные между компьютерами в обход локальной сети. Да и большинство пользователей не оценит таких нововведений и запишет вас в черный список заклятых друзей. К тому же есть желание работать спокойно, а не тратить все силы на войну с пользователями, поэтому нужно искать какой-то другой путь. Мне, как и многим другим администраторам, хотелось бы иметь возможность более аккуратно и гибко управлять доступом пользователей к устройствам, установленным в компьютере, чем это позволяет делать административный интерфейс, встроенный в Windows-системы. Также очень хочется, чтобы система безопасности автоматически меняла свои настройки в зависимости от даты и времени. В решении именно таких проблем нам должен помочь DeviceLock. Судя по документации, поставляемой вместе с программой, она может контролировать и разграничивать доступ пользователей к следующим устройствам: ! Дисководы гибких дисков. ! Магнито-оптические дисководы. ! CD-ROM. ! DVD. ! ZIP. ! Все виды устройств, подключаемых через USB. ! Инфракрасный порт. ! Устройства, подключаемые через FireWire. ! Серийные или параллельные порты. ! BlueTooth и WiFi-адаптеры . ! Накопители на магнитных лентах.
отличается, кроме временного ограничения и изредка появляющегося на экране напоминания с предложением зарегистрироваться. Итак, дистрибутив размером в 1.6 Мб был довольно быстро скачан. После чтения документации, которую можно взять на том же сайте, было выяснено, что программа предназначена для работы в системах Windows NT/2000/XP/2003. Для машин, функционирующих под управлением версий Windows 9x и Windows Millenium, требуется DeviceLock Millennium Edition. Для проведения тестов были выбраны две машины: одна с Windows 2000 Professional, а вторая с Windows Server 2003. Пришло время пробовать, какова на вкус эта новинка. Инсталляция прошла легко и без каких-либо проблем. Комплект DeviceLock состоит из двух частей: ! DeviceLock Service – сервис, предоставляющий интерфейс локального или удаленного управления DeviceLock. ! DeviceLock Manager – программа, позволяющая системному администратору управлять всем комплексом. Поэтому стоит обратить внимание на один интересный момент: если во время установки выбрать вариант «custom», то появится возможность поставить на машину только DeviceLock Service без DeviceLock Manager. Таким образом, на пользовательских машинах можно расставить только сервис, отвечающий за разграничение полномочий, а управлять всем этим хозяйством через сеть с помощью менеджера. Также доступен режим с автоматической локальной инсталляцией. Чтобы воспользоваться этой возможностью, нужно положить в директорию с дистрибутивом файл devicelock.ini и запустить программу установки с ключом –s. Такой подход позволит поручить процедуру обхода пользовательских машин и установку программы младшему администратору без риска, что он может случайно сделать что-то не так. Более подробно почитать о том, каков формат файла devicelock.ini и что нужно в него записать, можно в прилагаемой к программе и доступной на сайте документации. Ну а для тех, кто умеет работать с Microsoft Systems Management Server (SMS), есть вариант автоматической установки программного обеспечения на целевые машины через сеть. Кстати, стоит отметить очень хорошее качество документации, взятой с сайта, а вот то, что поставляется в пакете с программой в качестве встроенной системы помощи, выглядит весьма скудно. Надеюсь, в ближайшей версии разработчики исправят этот досадный недостаток. По окончании инсталляции программа выводит на экран вот такой диалог.
Заинтересовавшись этой программой, я отправился на сайт http://www.protect-me.com/ru/dl/download.html и скачал 30 дневную пробную версию. От платной она ничем не
№3(16), март 2004
39
безопасность В котором можно выбрать настройки по умолчанию. На первый взгляд, они выглядят довольно разумно. После завершения инсталляции можно увидеть, что в системе появилась новая служба.
писи, касающиеся устройства CD-ROM. Выбираем нашего многострадального пользователя «Петров» и с помощью мыши расставляем на календаре метки, указывающие, когда во время недельного цикла доступ должен быть отключен. К сожалению, автор программы не предусмотрел кнопок, позволяющих одним нажатием запретить или разрешить целый день. Надеюсь, в следующей версии это будет исправлено, а пока придется занудно щелкать мышью.
Первый запуск утилиты особых сюрпризов не приносит, так как интерфейс программы понятен и прост: слева – дерево машин, справа – список устройств.
Ну что же, давайте для начала запретим пользователю «Петров» работать с CD-ROM в обеденный перерыв по будним дням и на весь день по субботам и воскресеньям. Делается это просто: выбираем нужное устройство и нажимаем на значок ключа с биркой. В ответ получаем диалог с настройками безопасности для данного устройства. Жмем кнопку «+ Add» и в появившемся списке выбираем нужных пользователей поодиночке или группами.
На следующем экране слева список групп и пользователей, для которых в наборе правил безопасности есть за-
40
Также обратите внимание на опцию «Allow eject» – с ее помощью можно запретить пользователю вынимать диск из CD-привода. Для устройств, на которые можно записывать данные, доступна опция «Allow Format», позволяющая запретить пользователю выполнять действия по форматированию устройства. Таким образом, можно защитить данные от случайного уничтожения не в меру любопытными пользователями. Закончив раздавать права, можно посмотреть, как только что выставленный запрет будет выглядеть с точки зрения подопытного пользователя. Говорим системе, что сегодня «Суббота», или устанавливаем часы на обеденное время и пытаемся получить доступ к CD-ROM от имени пользователя «Петров».
Как видите, в этом временном интервале наш запрет действует надежнее любого замка, а как только часы перейдут в разрешенный диапазон, пользователь снова сможет работать с CD-ROM. Также, кроме всего прочего, хотелось бы отметить
безопасность очень полезное свойство программы, позволяющее легко и надежно разграничить полномочия пользователей при работе с FireWire и USB-портами. К сожалению, стандартными средствами администрирования Windows решить эту проблему невозможно, потому что для добавления в систему вышеуказанных устройств не требуется иметь каких либо особых привилегий, а это значит, что любой мало-мальски грамотный пользователь может подключить свои собственные устройства и спокойно уносить домой корпоративные документы. В отсутствии DeviceLock единственным надежным решением было бы полное отключение таких портов, но как я уже говорил выше, нам такой вариант не подходит. Наигравшись вдоволь с разными комбинациями правил, я решил проверить, насколько удобным будет применение DeviceLock в масштабе предприятия. Часто случается так, что в организации есть некоторое количество машин, которыми пользуется определенный круг работников, и все они должны быть настроены примерно одинаково. Для малой сети из нескольких машин описать все правила для всех пользователей на каждом компьютере – процедура не особенно приятная, хотя ради обеспечения собственного дальнейшего спокойствия можно и потерпеть. В крайнем случае это неблагодарное занятие можно поручить тому же младшему администратору. А вот в средних или больших сетях такое мероприятие может превратиться в продолжительную пытку. И самое главное неудобство кроется в повседневном обслуживании такой системы. С течением времени одни люди увольняются, а других нанимают на освободившиеся должности. Неужели из-за этого придется удалять или модифицировать вручную на всех компьютерах правила, касающиеся конкретного пользователя? Вот тут-то на сцену выходит одна из самых мощных способностей DeviceLock, называемая batch permissions. С ее помощью мы за несколько минут решим эту проблему. Для того чтобы воспользоваться этой возможностью, жмем кнопку с изображением двух ключей или проходим через меню File → Batch Permission. В появившемся диалоге добавляем в список компьютеры, на которые нужно скопировать создаваемые правила.
Стоит отметить, что список машин можно создать с помощью графического интерфейса или загрузить из предварительно созданного файла. Следующим шагом создаем список пользователей, на которых будут распространяться наши правила. Затем помечаем галочками нужные устройства, настраиваем временные интервалы, разрешения и прочие опции. Нажав кнопку «Set permissions», наслаждаемся процессом. Обратите внимание на опцию «Install/Update Service», она очень полезна для нас, если во время копирования правил на целевой машине будет найдена устаревшая версия DeviceLock Service, то программа выполнит всю черную работу за нас, и нужный компонент будет обновлен до текущей версии. В течение нескольких дней, когда я самыми разными способами тестировал работу Batch permisions, была замечена всего одна ошибка. Видимо, во время копирования правил через сеть, что-то пошло не так, и я получил вот такую ошибку.
Судя по надписи, сработала защита от ошибок RPC, и никаких критичных действий выполнено не было. Повторный запуск процедуры завершился удачно. Кроме вышеописанной ошибки никаких других проблем обнаружено не было. Отдельным словом благодарности стоит отметить удобство управления сервисами DeviceLock работающими на удаленных машинах. Два раза щелкнув на имени компьютера, который нужно настроить, и введя пароль, можно управлять им так же просто, как и локальным. Кажется, что иногда можно даже запутаться и забыть, какой именно машиной мы сейчас управляем, но, как ни странно, этого не происходит.
В целом можно считать, что программа показалась мне довольно приятным в обращении и полезным инструментом. Используя ее с умом, системный администратор сможет добавить в структуру безопасности предприятия удобный в обращении, достаточно надежный и простой в обслуживании слой защиты. Надеюсь, короткий экскурс, предпринятый мной с целью изучения и описания возможностей DeviceLock, был вам интересен.
№3(16), март 2004
41
безопасность
ТРИ ПОРОСЁНКА Snort: «НИФ-НИФ», «НУФ-НУФ» И «НАФ-НАФ». НАСТРОЙКА НЕСКОЛЬКИХ СЕНСОРОВ Snort С ПОМОЩЬЮ SnortCenter
Èëëþñòðàöèÿ Ì.ß.Ðóäà÷åíêî
ПАВЕЛ ЗАКЛЯКОВ 42
безопасность Прежде чем перейти к практическим вопросам использования нескольких сенсоров на базе IDS Snort, рассмотрим теоретические аспекты этой задачи. Для этого оценим трудности, возникающие при различных способах реализации, и создадим модель, по большей части лишённую замеченных недостатков. «Одна голова хорошо, а две лучше», – подумал змей Горыныч, убегая от Ильи Муромца. Так и в вопросах обнаружения атак: информация лишней не бывает, больше сенсоров – меньше вероятность пропуска атаки. Конечно, эффективность подключения новых сенсоров, начиная с некоторого числа, не даст такого прироста эффективности, как вначале, но это уже другой вопрос. При использовании нескольких сенсоров наиболее удобным способом ведения логов будет использование одной или нескольких БД. Подробнее об этом можно прочитать в [4]. Задача заставить два сенсора вносить свои записи в одну БД не представляет из себя никакой сложности. Об этом не было рассказано ранее, но можно догадаться, что необходимо в третьей секции конфигурационного файла snort.conf, отвечающей за вывод данных, прописать всего лишь в одной строчке другое имя хоста с БД, номер порта, логин и пароль. Далее, если доступ к БД не закрыт каким-нибудь пакетным фильтром или МЭ по пути, то никаких проблем быть не должно. Даже ACID будет исправно показывать статистику от двух и более сенсоров. Работа такой системы только на первый взгляд кажется прозрачной и беспроблемной. По мере увеличения времени эксплуатации системы неизбежно возникнут различные вопросы. Вот некоторые из них, которые видно невооружённым глазом сразу до начала эксплуатации: ! Обновления сигнатурной БД, в нашем примере это правила, по которым обнаруживаются атаки. ! Совместный анализ данных. Данные в БД заносятся из разных мест, а при анализе этот факт никак не учитывается. ! Доверенность сенсоров и устойчивость самой системы обнаружения атак к атакам на неё. ! Контроль за сенсорами. Таким образом, казалось бы, простенькая идея замены одного адреса хоста в файле конфигурации оборачивается большим списком вопросов, не на все из которых можно найти однозначный ответ. Давайте остановимся и частично рассмотрим проблемы и пути решения первого вопроса. При отсутствии каких-либо целенаправленных решений поставленный вопрос решается неудобно, но просто. Администратор подключается к компьютеру стандартными средствами администрирования, например, с помощью ssh или telnet. Руками вносит изменения в конфигурационный файл и перезапускает snort. Потом подключается к другому хосту, где установлен другой сенсор и повторяет операцию. Согласитесь, что при большом количестве узлов ситуация не очень удобная. Поэтому администратор, чтобы избавить себя от подобной рутинной работы, один раз автоматизирует процесс – пишет скрипт на bash, который периодически запускается через cron, скачивает c помощью wget новые правила с сайта www.snort.org и устанавливает
№3(16), март 2004
их в систему. Благо правила всё время находятся в одном и том же месте на сайте, а имя файла с последними правилами остаётся неизменным. Либо http://www.snort.org/dl/ rules/snortrules-stable.tar.gz (* см.ниже) – стабильная ветка, либо http://www.snort.org/dl/rules/snortrules-current.tar.gz (* см. ниже) – активно развивающаяся. Во время процесса установки в систему правила разархивируются, распаковываются, возможно, как-то проверяются, помещаются на место старых правил, в конфигурационный файл, возможно, вносятся изменения и происходит перезапуск демона snort, чтобы внесённые изменения вступили в силу. Таким образом, система работает с завидной стабильностью, присущей Linux/*BSD, некоторое, возможно, и продолжительное время. Позже планомерно появляются новые хосты с сенсорами, меняются правила и происходят другие мелкие события. В общем, всё работает. Но вот наступает тот самый момент, когда в алгоритме работы системы наступает сбой либо вероятность его возникновения велика. Нет, это не правила неверно скачались. И не изменилось их местоположение, хотя и такое частенько бывает. По этой причине вверху у ссылок стоят звёздочки, ссылки уже не действительны. Так, недавно обновились адреса и форматы правил для Snort после выхода версии 2.1.1-RC1 из серии 2.1.x. Сейчас для разных серий надо скачивать разные правила, в результате имеем следующие реальные ссылки для скачивания: ! http://www.snort.org/dl/rules/snortrules-snapshotCURRENT.tar.gz ! http://www.snort.org/dl/rules/snortrules-snapshot-2_1.tar.gz ! http://www.snort.org/dl/rules/snortrules-snapshot-2_0.tar.gz Старые же правила переехали в поддиректорию: http:// www.snort.org/dl/rules/old. При этом на сайте даже нет мягких ссылок для обратной совместимости на новое местоположение старых правил. При попытке загрузить файлы по ссылкам выше со звёздочками выдаётся ошибка о том, что нет таких файлов. В ситуации выше будем считать, что система проверки правил их не установила и послала письмо-уведомление администратору о возникшей ошибке. Это будет не у всех, подобную обратную связь каждому придётся налаживать вручную. Но что же произошло, если даже и не система отказала из-за сбоев в электропитании? А просто в самой IDS Snort нашли очередную ошибку, например, для временного решения которой необходимо отключение того или иного препроцессора в системе обработки трафика. То есть просто-напросто надо внести изменения в конфигурационные файлы, например, закомментировать одну строчку. И вот ситуация повторяется, администратор залезает и вручную правит правила на каждом из сенсоров. А тут ситуация усугубляется тем, что раньше он работал один, а к моменту сбоя штаты разрослись, и администраторов стало много. Так что для установки всего лишь одного знака комментария необходимо всем знать пароль суперпользователя и прочие неудобства. Какой же может быть выход? Наиболее оптимальным решением для данного случая было бы использование некой единой конфигурационной БД с правилами для всех сенсоров. Удобно, когда все правила в одном месте, соответственно, для внесения изменений уже требуются права толь-
43
безопасность ко на доступ к конфигурационной БД с правилами, то есть нет того случая, когда выдаются излишние права и пользователь может сильно повредить работоспособность системы. Всё, казалось бы, хорошо, но только за любое удобство приходится чем-то расплачиваться. В данном случае нам придётся решать проблемы связи сенсоров с центральной конфигурационной БД. Проблем здесь не меньше, а именно: ! выбор того, кто с кем будет связываться: база данных с сенсорами или сенсоры с БД; ! защита от пассивных атак (прослушивание трафика); ! защита от активных атак, подмена правил и пр. На наше счастье, нет необходимости придумывать данную систему с нуля, так как уже имеется пакет SnortCenter [1], изучением и настройкой которого мы займёмся далее.
Коротко Основная идея SnortCenter – упростить конфигурирование (в том числе обновление правил) и диагностику работоспособности большого числа сенсоров. При необходимости могут конфигурироваться сенсоры на базе Windows NT платформы. Под диагностикой понимается информация о том, запущен и работает демон Snort или нет. Анализом и сбором данных SnortCenter не занимается, поэтому всё, что было рассказано в [4] о внесении записей сенсорами в единую БД и о том, как эту БД просматривать, нам пригодится. SnortCenter – это не зависимое от средств анализа решение, скорее – дополняющее его. Для удобства пользователей имеется SnortCenter ACID Plugin, позволяющий красиво интегрировать веб-интерфейсы SnortCenter и ACID.
Ðèñóíîê 1. Íàãëÿäíàÿ ñõåìà âçàèìîäåéñòâèÿ ñåíñîðîâ ñ öåíòðîì
44
Из чего состоит SnortCenter? 1. Из управляющей консоли SnortCenter Management Console – программы, написанной на PHP и осуществляющей взаимодействие: ! с сенсорами посредством агентов; ! с правилами посредством БД; ! с пользователем посредством веб-интерфейса. 2. Из агентов SnortCenter Sensor Agents, запускаемых на компьютерах-сенсорах с установленным Snort. Агенты – это автономные (не требуют наличия веб-сервера) CGIпрограммы на Perl, которые позволяют пользователю или SnortCenter Management Console через веб-интерфейс взаимодействовать со Snort и его файлами конфигурации. Для защиты соединений от прослушивания используется SSL-библиотека для perl SSLeay [5]. Если попытаться наглядно представить работу данной системы, должна получиться примерно следующая схема (см. рис. 1). Данная система не является безопасной с точки зрения атак на неё и поэтому требует использования дополнительных средств. Наиболее простым и эффективным средством снижения опасности атак на систему может быть использование пакетных фильтров (на схеме они не изображены), например, iptables. В этом случае можно избежать большинства атак, осуществляемых без подмены IP-адреса. Несколько подробнее о вышеописанном способе и его недостатках написано далее. Если вы решили создать у себя систему обнаружения атак с несколькими сенсорами и поняли, что вышеописанная схема управления конфигурационными файлами в целом вам
безопасность подходит или данная реализация всё же лучше, чем её отсутствие, то давайте приступим к её установке и настройке.
Установка Snort Выберем три узла, которые будут сенсорами, назовём их, например, «Ниф-Ниф», «Нуф-Нуф» и «Наф-Наф» и попробуем создать схему, аналогичную приведённой выше, для этого, как и ранее, нам потребуется скачать и установить Snort с поддержкой ведения логов в БД (подробнее см. [4]) на каждый из узлов-сенсоров. Те, у кого Snort уже установлен, могут пропустить несколько шагов, со всеми остальными скачиваем последнюю версию Snort: # wget http://www.snort.org/dl/snort-2.1.1.tar.gz # wget http://www.snort.org/dl/snort-2.1.1.tar.gz.md5
В то время как на другом компьютере конфигурация прошла без проблем. Первая мысль, что пришла в голову – поискать на нём файл pcre.h, запустив: # find / -name pcre.h
В результате поиска были получены две строки: /usr/include/pcre/pcre.h /progi/snort/snort-2.1.1/src/win32/WIN32-Includes/pcre.h
после чего возникла мысль узнать, какой rpm-пакет содержит нужный файл, после чего была выполнена следующая команда: # rpm -qf /usr/include/pcre/pcre.h
Сравниваем значение хеша выданного командами: # cat snort-2.1.1.tar.gz.md5 # md5sum snort-2.1.1-RC1.tar.gz
которая и сообщила, что нужный нам пакет – это pcredevel-3.9-2, который успешно нашёлся на втором диске RedHat 7.3 и был установлен командой: # rpm -ihv pcre-devel-3.9-2.i386.rpm
Распаковываем содержимое архива snort-2.1.1.tar.gz куда-нибудь, например, в уже имеющуюся директорию /progi: # tar -zxvf snort-2.1.1.tar.gz -C /progi
после чего конфигурирование, запущенное повторно, прошло без проблем. После конфигурирования необходимо скомпилировать Snort командой:
После заходим в /progi/snort-2.1.1:
# make
# cd /progi/snort-2.1.1
И установить командой:
и запускаем конфигурирование с опцией --with-mysql, не забыв о том, что для осуществления этого шага нам необходимо, чтобы в системе уже стояла библиотека libpcap, например, licpcap-0.6.2-12, и часть файлов от MySQL, в частности пакеты mysql-3.23.58-1.73 и mysql-devel-3.23.58-1.73. Подробнее см. [4]. # ./configure --with-mysql
Замечание. В процессе обновления Snort на одном из сенсоров с версии 2.0.4 до версии 2.1.1 во время запуска команды конфигурации новой версии выдалась следующая ошибка об отсутствии библиотеки pcre (Perl Compatible Regular Expressions [9]).
# make install
В процессе установки, если вы не задавали дополнительных ключей, у вас будет установлено всего лишь 2 файла: /usr/local/bin/snort – сама программа и man-страница к ней /usr/local/man/man8/snort.8. Далее следует создать директорию /etc/snort, если вы ставите Snort на данный компьютер в первый раз, или удалить все файлы и поддиректории оттуда, если они у вас остались от предыдущих версий. То же самое следует сделать с директорией /var/log/snort. В целом нам эта директория не понадобится, но если вдруг не получится вести логи в БД, то она может нам пригодиться в процессе отладки. Аналогичным образом следует установить Snort на все будущие сенсоры. Следующим шагом мы осуществим установку агентов (SnortSensor Agent) на наши сенсоры. После того как мы убедимся, что они работают, мы установим и настроим консоль управления SnortSensor (SnortCenter Management Console), через которую загрузим на все сенсоры их файлы конфигурации и осуществим дополнительную отладку агентов, если это понадобится.
Установка SnortSensor Agent Перед установкой агента скачиваем необходимый для его работы модуль для perl Net::SSLeay [5]. (Если модуля нет – не будет поддерживаться закрытое SSL-соединение с агентом.)
№3(16), март 2004
45
безопасность # wget http://symlabs.com/Net_SSLeay/Net_SSLeay.pm-1.21.tar.gz
Распаковываем содержимое архива Net_SSLeay.pm1.21.tar.gz куда-нибудь, например, в уже имеющуюся директорию /progi: # tar -zxvf Net_SSLeay.pm-1.21.tar.gz -C /progi
После заходим в появившуюся директорию по имени и номеру версии модуля: # cd /progi/Net_SSLeay.pm-1.21
и запускаем # perl Makefile.PL
либо # ./Makefile.PL
Запущенный perl-скрипт проверяет наличие в системе OpenSSL и его версию, создаёт необходимые для дальнейшей установки Makefile файлы. OpenSSL необходим для нормальный работы Net_SSLeay. В случае необходимости выдаются рекомендации по обновлению версии. Замечание 1. Следует отметить, что не всегда лучше иметь самый последний номер того или иного пакета, в каждом конкретном случае следует разбираться отдельно. Так, например, тот же Red Hat очень любит выпускать исправления к старым версиям. В результате получается, что номер версии пакета остаётся старый, а ошибок в пакете уже нет. Замечание 2. Для компиляции модуля Net_SSLeay необходимо, чтобы также был установлен пакет openssl-devel, хотя его наличие не проверяется. Сделать это можно, например, командой:
диться, что модуль SSL установился правильно, запустите: # perl -e 'use Net::SSLeay'
Если не будет никаких сообщений об ошибках, то это значит, что SSL-модуль установился правильно. После того как модули установлены, можно приступить к непосредственной установке SnortSensor Agent. Для этого скачиваем последнюю версию из [2]. # wget http://users.pandora.be/larc/download/ ↵ snortcenter-agent-v1.0-RC1.tar.gz
Скорее всего версия у вас будет та же самая, так как уже более года не обновляются новости и не выпускаются новые версии. В процессе отладки продукта мы встретимся с некоторыми проблемами, часть которых попробуем решить своими силами. К сожалению, попытки связаться с автором («Stefan Dens» <larc@pandora.be>), чтобы скоординировать усилия и, возможно, способствовать выходу новой версии без замеченных недостатков, не привели к успеху. На форуме от Snort вопросами SnortCenter не интересуются. В российской части, например на sysadmins.ru, поиск чего-нибудь по ключевому слову snortcenter абсолютно бесполезен. Можно констатировать, что проект «не отполирован до конца». Несмотря на подобное равнодушие к нему, в нём можно найти некоторые полезные свойства, о которых можно прочитать ниже. После скачивания архив необходимо куда-нибудь распаковать, например в /progi: # tar -zxvf snortcenter-agent-v1.0-RC1.tar.gz -C /progi
После запуска команды следует зайти в появившуюся директорию /progi/sensor: # cd /progi/sensor
# rpm -ihv openssl-devel-0.9.6b-35.7.i386.rpm
Если пакет openssl-devel у вас не будет установлен, то в процессе компиляции вы получите следующую ошибку:
Далее следует запустить скрипт setup.sh для конфигурации сенсора посредством ответов на вопросы: # ./setup.sh
Для компиляции запускаем: # make
После успешной компиляции запускаем: # make install
для установки модулей и man к ним. Для того чтобы убе-
46
Появится следующая картинка (см. рис. 2, 3). На запрос «Config file directory» вместо /progi/sensor/ conf следует указать /etc/snort / – то место, где будут располагаться и куда будут записываться конфигурационные файлы, необходимые для запуска Snort. На запрос «Log file directory» вместо /progi/sensor/log следует указать /var/log/snort – это место, куда будут вестись логи. На запрос расположения интерпретатора Perl «Full path to perl» нажмите просто {ENTER}, если вы не меняли его месторасположение и имя на отличные от приведённых по умолчанию /usr/bin/perl. То же самое и по поводу Snort – нажмите просто {ENTER}. На запрос о пути к правилам Snort «Snort Rule config file directory» введите /etc/snort вместо предлагаемых /progi/ sensor/rules/.
безопасность
Ðèñóíîê 2. Êîíôèãóðèðîâàíèå ïîñðåäñòâîì îòâåòîâ íà âîïðîñû setup.sh
№3(16), март 2004
47
безопасность
Ðèñóíîê 3. Êîíôèãóðèðîâàíèå ïîñðåäñòâîì îòâåòîâ íà âîïðîñû setup.sh (ïðîäîëæåíèå)
Замечание. Несмотря на то что в предыдущих настройках мы располагали правила в /etc/snort/rules, сейчас, исходя из соображений удобства, мы этого делать не будем. Далее задаются вопросы, необходимые для настройки собственного веб-сервера. Уточняется порт, на котором будет веб-сервер агента «Sensor port», лучше всего его оставить без изменений, если у вас нет других соображений на этот счёт, нажав {ENTER}. (По умолчанию 2525). Далее спрашивается, какой IP-адрес следует прослушивать. Это нужно на тот случай, если у вас несколько IPадресов на одном интерфейсе или более сложная схема. По умолчанию следует оставить «any», и тогда будут прослушиваться только те адреса, которые связаны с конкретным интерфейсом. Это первое неудобство агентов, что их следует привязывать к интерфейсам. Это не самое страшное неудобство, и об этом мы поговорим чуть ниже. Пока же на запрос «If this host has multiple IP addresses, the server can be configured to listen on only one address (default any):» нажмём просто {ENTER}. Далее программа настройки запрашивает логин пользователя, которому будет разрешено в дальнейшем подключаться к веб-серверу. Для начала на запрос «Login name (default admin)» можно нажать просто {ENTER}, однако если вы хотите несколько повысить защищённость, то придумайте своё имя, которое вам придётся соответственно заменить и в других местах самостоятельно, где это понадобится.
48
Замечание. Реальные пользователи в системе не заводятся. Заведённые пользователи с хэшами от паролей находятся в /etc/snort/sensor.users. В силу особенностей реализации не используйте символы «:» и «@» в логине и пароле во избежание ошибок. На запросы «Login password:» и «Password again:» придумайте и введите два раза пароль: ваш_пароль№7 для доступа вышеобозначенного пользователя к веб-серверу. Будьте внимательны, во время ввода пароля в соответствии с одним из требований безопасности System V звёздочки вместо вводимых символов не отображаются. В случае несовпадения паролей программа выдаст ошибку:
и необходимо будет запустить конфигурацию ещё раз: # ./setup.sh
и ответить на вышеописанные вопросы заново. После удачного ввода пароля появится вопрос, как называется хост, на котором установлен сенсор. «Sensor host name (default %имя_вашего_хоста%):» Можно оставить то имя, что указано по умолчанию, нажав {ENTER}, а можно написать что-то новое, вроде Nif-Nif или Nuf-Nuf, чтобы както различать сенсоры в дальнейшем. На вопрос об использовании SSL отвечаем «y» и жмём {ENTER}. Далее нас спрашивают о диапазоне IP-адресов, с ко-
безопасность торых можно будет осуществлять доступ к агенту. Это дополнительная мера защиты, используемая в добавление к авторизации. При необходимости всё то же самое более гибко можно сделать с помощью какого-нибудь пакетного фильтра, вроде iptables, однако одно другому не мешает и на запрос «Allowed IP addresses» лучше ввести IP-адрес или имя центра управления сенсорами. При необходимости ввести несколько адресов разделяйте их пробелами, следуя выведенной подсказке. Следующим будет вопрос о том, следует ли запускать агента автоматически при загрузке системы «Start Sensor at boot time (y/n):», то есть необходимо ли создать соответствующие файлы в /etc/rc.d/... Отвечаем на этот вопрос «y» и жмём {ENTER}, в противном случае нам придётся каждый раз после перезагрузки системы по тем или иным причинам запускать агентов руками, что может быть не очень удобно. После ответа на все вопросы осуществляется запуск агента, и фактически он уже готов работать. В директории /etc/snort у вас появится 11 файлов и 2 директории с необходимыми файлами для работы агента (см. рис. 4). В частности, будут созданы файлы для запуска, остановки и деинсталляции, так что совсем не обязательно запускать и останавливать работу агента через /etc/ rc.d/init.d/sensor.
Ðèñóíîê 4. Âèä äèðåêòîðèè /etc/snort ïîñëå óñòàíîâêè SnortSensor Agent
# iptables -I INPUT -p tcp -s $REMOTE_IP --sport 1024:65535 ↵ -d $SENSOR_IP --dport 2525 -j ACCEPT # iptables -I OUTPUT -p tcp -s $SENSOR_IP --sport 2525 ↵ -d $REMOTE_IP --dport 1024:65535 -j ACCEPT
Переменные $REMOTE_IP и $SENSOR_IP следует заменить нужными значениями. Данные правила не учитывают направления установления соединения, но при необходимости их можно подправить, ещё больше «закрутив гайки». После захода на веб-страничку агента будет запрошен логин и пароль. И выдастся следующая картинка:
Нам этого вполне достаточно, пытаться что-то запустить на данный момент есть бессмысленное занятие, так как у нас ещё нет конфигурационного файла, с которым бы следовало запускать Snort. Аналогичным образом устанавливаются и проверяются SnortSensor Agent на других узлах. Для того чтобы создать необходимые конфигурационные файлы и запустить Snort, нам необходимо теперь заняться центром управления, для этого установить на него непосредственно консоль управления, так называемую SnortSensor Management Console, и настроить её на совместную работу с БД правил и сенсорами, далее необходимо создать правила для сенсоров, передать их на узлы. После этого на узлах-сенсорах будут созданы соответствующие им файлы конфигурации, и можно будет попытаться запустить Snort.
Запустив: # /etc/rc.d/init.d/sensor status
можно убедиться, что агент запущен и работает. Если на компьютере будущего центра имеется веб-браузер или вы заходите с другого компьютера, указанного в списке разрешённых для доступа к веб-серверу, и правила пакетного фильтра позволяют проходить пакетам, то вы можете запустить браузер и подключиться к агенту через защищённый веб-интерфейс, набрав просто адрес «https://адрес_агента:2525». Проверить работу агента таким образом желательно, чтобы не искать после ошибку, потратив большее количество времени. Если вы не позаботились ранее о возможности подключения к агенту с других хостов, хотя бы на время настройки системы, то, возможно, вам потребуется дописать разрешение на нужный вам хост в файле /etc/snort/miniserv.conf через пробел после того, что написано у параметра «allow=». Удалить лишние разрешённые хосты можно там же. Для разрешения прохождения пакетов через пакетный фильтр iptables, настроенный на политику запрета по умолчанию, следует указать два следующих правила:
№3(16), март 2004
Установка и настройка консоли управления SnortSensor Management Console По идее, центр управления лучше установить на отдельном компьютере, но так как на сам центр могут также совершаться атаки, то лучше и на нём установить сенсор для их обнаружения. В результате получится конфигурация, когда центральная консоль управления устанавливается на одном из сенсоров. Для начала установки скачиваем из [2] последнюю версию SnortSensor Management Console, способную работать со Snort v.2.x. # wget http://users.pandora.be/larc/download/ ↵ snortcenter-v1.0-RC1.tar.gz
Далее распаковываем содержимое архива туда, где находятся файлы веб-сервера, например, в директорию /var/ www/html: # tar -zxvf snortcenter-v1.0-RC1.tar.gz -C /var/www/html
Далее, чтобы не запутаться, распаковавшуюся только
49
безопасность что директорию /var/www/html/www переименуем в /var/www/ html/snortcenter:
! $DB_dbname – переменная, определяющая имя БД, в
# mv /var/www/html/www /var/www/html/snortcenter
Затем нам необходимо отредактировать файл config.php, для этого либо запускаем:
!
# mcedit /var/www/html/snortcenter/config.php
либо жмём F4 на нужном файле в mc (midnight commander). Если у вас не установлен mc, то редактируем файл с помощью vi: # vi /var/www/html/snortcenter/config.php
Предполагается, что на центральном компьютере уже установлен ACID и все необходимые для его работы компоненты, в частности php, php-mysql и ADODB – абстрактный класс доступа к базам данных, написанный на PHP [7]. Подробнее об установке ACID см. [4]. В процессе редактирования конфигурационного файла необходимо правильно определить следующие переменные: ! $DBlib_path – переменная отвечает за путь к ADODB, если следовать установке, описанной в [4], то вместо $DBlib_path = «./adodb/»; следует написать $DBlib_path = «../adodb/»; ! $DBtype – переменная, отвечающая за тип БД, так как мы работаем пока только с БД MySQL, то и оставим её без изменения: $DBtype = «mysql»;
50
!
которой находятся все правила наших сенсоров, при желании имя для БД можно сменить, но тогда и далее, когда мы будем создавать эту БД, придётся ей указать другое имя. Поэтому оставляем это значение без изменения $DB_dbname = «snortcenter»; $DB_host – переменная, определяющая имя (адрес) хоста, на котором стоит БД с правилами. При желании БД может располагаться не обязательно на том же хосте, где находится SnortSensor Management Console. В случае если хосты будут разными, то помимо удобств это добавит проблем, а именно необходимо будет дополнительно осуществлять защиту соединений от прослушивания и навязывания информации между БД и управляющей консолью. В нашем случае хосты совпадают, поэтому оставляем без изменений: $DB_host = «localhost»; $DB_user – переменная, определяющая, от имени какого пользователя следует подключаться к базе данных с именем $DB_dbname. По умолчанию в программе предполагается использование имени root – суперпользователя БД MySQL. Если оставить эту переменную без изменений, то в качестве пароля в следующей переменной придётся задать ваш_пароль№2 см. [4]. В принципе это не очень хорошо, так как SnortCenter Management Console получит излишние права над всеми БД, хотя ей всего лишь достаточно прав на одну лишь БД snortcenter ($DB_dbname). Для того чтобы лишний раз не рисковать, в качестве $DB_user мы напишем пользователя snortcenteruser $DB_user = «snortcenteruser»; а потом создадим такого пользователя в БД MySQL.
безопасность ! $DB_password – переменная, определяющая пароль
!
!
!
!
пользователя БД $DB_user. Если ранее вы оставили пользователя root, запишите ваш_пароль№2, в противном случае необходимо придумать ваш_пароль№6 и записать: $DB_password = «ваш_пароль№6»; $DB_port – переменная определяет порт, на котором работает БД и к которому следует подключаться. По умолчанию в БД MySQL используется порт 3306, если вы его не меняли, то оставьте значение этой переменной пустым, будет использоваться именно этот порт. $hidden_key_num – в эту переменную необходимо записать придуманное вами случайное число. Данное случайное число используется в дальнейшем в системе аутентификации для шифрования значений, хранящихся в cookies. Насколько я понимаю, в системе применяется некоторая нормализующая функция перед использованием числа, поэтому его разрядность не имеет большого значения, так что можно записать хоть «5235763723333». На счёт оптимального числа разрядов в случайном числе сказать что-либо обоснованное мне сложно. $snortrules_url – данная переменная определяет URL файла, из которого будут браться «новые» правила. Данный файл будет скачиваться при выборе в дальнейшем пункта меню «обновление через Интернет». Если бы не менялся формат правил, то данную переменную следовало бы оставить без изменения, но так как для новых версий Snort нужны правила в новом формате, то если её не поменять, то обновление работать не будет в связи с тем, что новые правила лежат в новом файле, поэтому вместо $snortrules_url = «http://www.snort.org/dl/ rules/snortrules-stable.tar.gz»; пишем $snortrules_url = «http://www.snort.org/dl/rules/snortrules-snapshot2_1.tar.gz». Если вас не устраивает обновление через Интернет, например, по соображениям безопасности, то некоторые рекомендации вы встретите ниже. $max_lines – данная переменная отвечает за число правил, помещающихся на одной странице, естественно, при маленьком значении переменной большое число правил ведёт к большому числу страниц, что неудобно. Лучше увеличить это значение, но если канал связи медленный, то делать число правил на странице большим не следует. Однако удобнее вначале сделать его большим, активировать все правила, а после придать параметру какоенибудь разумное значение, например, $max_lines = 50 вместо установленных по умолчанию $max_lines = 12.
В конфигурационном файле встречаются и другие переменные, но для нас они менее существенны, поэтому мы их не рассматриваем. Подробнее обо всех переменных см. [8]. После того как управляющая консоль SnortCenter настроена, нам необходимо: ! создать БД snortcenter; ! создать пользователя snortcenteruser; ! задать пароль ваш_пароль№6 этому пользователю; ! задать необходимые права данному пользователю для доступа к его БД. Для этого подключаемся к БД MySQL от имени суперпользователя этой БД.
№3(16), март 2004
# mysql -u root -p
На запрос пароля вводим ваш_пароль№2 (см. [4]). Далее даём команды: mysql> CREATE DATABASE snortcenter; mysql> grant CREATE,INSERT,SELECT,DELETE,UPDATE ↵ on snortcenter.* to snortcenteruser@localhost; mysql> grant CREATE,INSERT,SELECT,DELETE,UPDATE ↵ on snortcenter.* to snortcenteruser; mysql> set password for ↵ 'snortcenteruser'@'localhost'=password('âàø_ïàðîëü¹6'); mysql> set password for ↵ 'snortcenteruser'@'%'=password('âàø_ïàðîëü¹6'); mysql> exit
Третью и пятую команды можно не давать, если у вас БД и управляющая консоль стоят на одном компьютере. В результате должно получиться нечто следующее:
Далее начинается всё самое интересное, а именно, визуальная настройка через веб-интерфейс. Для этого заходим через любой графический браузер на адрес нашего центра, где установлена управляющая консоль SnortCenter (http://адрес_SnortCenter/snortcenter/, а лучше, если ваш вебсервер поддерживает защищённое соединение https:// адрес_SnortCenter/snortcenter/). Программа быстро определяет, что была запущена в первый раз, и создаёт необходимые для её работы таблицы. Также среди строчек мы можем увидеть строчку о создании пользователя:
из которой мы можем узнать логин и пароль для доступа к SnortSensor Management Console в дальнейшем. Если подождать некоторое время либо набрать вышеуказанный URL повторно, то мы попадём на страничку, где будут спрашиваться вышеуказанные login и password. В дальнейшем мы сменим пароль на другой, а пока вводим «change».
После входа нам необходимо записать в БД новые правила, обычно для этого рекомендуется зайти в меню Admin → Import/Update rules → Update from Internet.
51
безопасность
После чего программа скачивает все правила из Интернета с адреса $snortrules_url, указанного в файле config.php. Иногда по соображениям безопасности сервера не имеют доступа к веб-сайтам, поэтому такое обновление может потерпеть неуспех. Если у вас подобный случай, то вместо обновления правил вас ждёт через некоторое время следующая картинка.
Выходов из этой ситуации два. Первый – скачать правила вручную, проверить и загрузить их на доверенный вебсервер в том виде, как они были изначально. После подправить $snortrules_url в config.php и, возможно, дописать пару разрешающих правил для iptables и проследовать указани-
Ðèñóíîê 5. Îøèáêà â ïðîöåññå çàãðóçêè
Ðèñóíîê 6. Îøèáêà, ñòðîêà äà¸ò ïîíÿòü ïðè÷èíó îøèáêè
52
ям выше, как будто вы загружаете правила из Интернета. Второй – так же вручную скачать правила, распаковать, проверить, создать из них единый текстовый файл. После использовать этот текстовый файл для записи правил в БД либо путём копирования всего его содержимого в соответствующую загрузочную область веб-формы (Admin → Import/Update Rules → Copy & Paste), либо путём обычной загрузки файла на сервер (Admin → Import/Update Rules → Upload file). Для второго необходимо включить в php поддержку загрузки файлов на сервер. Замечание. После выхода очередной версии Snort серии 2.1.x при попытке загрузить новые правила мной была обнаружена несовместимость правил от Snort 2.1.x со SnortCenter. Возникла следующая ошибка в процессе загрузки (см. рис. 5). Покопавшись в PHP-файлах SnortCenter, я раскомментировал 293-ю строку: //echo "$sql<BR>";
безопасность которая находится перед: $result = $db->acidExecute($sql); $result_a = $db->acidExecute("SELECT max(id) FROM preprocessor"); $myrow = $result_a->acidFetchRow(); $update_rule_count[1] = 'add-spp'; $update_rule_count[2] = $myrow[0];
В результате была получена следующая картинка после запуска (см. рис. 6). После этого, глядя на «global \», возникла мысль, что проблема, скорее всего, связана с переносом длинных строк в файлах конфигурации. В разборщике правил для SnortCenter, видимо, забыли учесть, что, начиная со Snort v.1.8, правила не обязательно должны писаться в одну
строчку, и что теперь они могут переноситься с помощью указания обратного слеша «\» в конце строки. После того как стала ясна причина ошибки, можно попытаться её исправить, для этого пишем небольшой скрипт на perl, который будет перенесённые строки собирать в одну строку. Создадим файл /var/www/html/snortcenter/convert_rules.pl следующего содержания: #!/usr/bin/perl while ($temp=<STDIN>){ if ($temp=~/^[^#].*\\\n$/) { chomp $temp; chop $temp; } print $temp; }
Ðèñóíîê 7. Ôðàãìåíò îêíà áðàóçåðà ïîñëå óñïåøíîé çàãðóçêè ïðàâèë
№3(16), март 2004
53
безопасность
Ðèñóíîê 8. Ïðàâèëà â ÁÄ, óáåäèìñÿ, ÷òî îíè åñòü
Ðèñóíîê 9. Äîáàâëåíèå ñåíñîðà
Не забудем придать атрибут запускаемости этому файлу. # chmod +x convert_rules.pl
При желании скрипт можно сократить, но тогда он будет менее наглядным. Скрипт делает следующее: читает построчно стандартный поток ввода и помещает прочитанное в переменную $temp. Далее эта переменная проверяется на наличие в конце строки обратного слеша и что эта строка не есть комментарий. Если обратный слеш есть и строка не начинается со знака «#» (какой смысл переносить комментарии), то осуществляется отрезание от строки символа её конца и обрат-
54
ного слеша, что приводит к тому, что последующая строка как бы приклеивается к текущей в общем потоке символов. Далее необходимо внести изменения в скрипт разбора файлов в нужном месте, чтобы наш файл оттуда вызывался перед тем, как правила попадут к разборщику. Для этого вносим изменения в строки 74 и 78 файла db_pars.php, отвечающие за загрузку правил с веб-сервера. Вносить изменения в файлы, загружаемые вручную также желательно, но не обязательно, так как в этом случае вы всегда имеете возможность их подправить, придав им нужный вид. Поэтому исправления будут только в двух местах, а не в большем количестве мест. Вместо:
безопасность
Ðèñóíîê 10. Ñîõðàíåíèå èíôîðìàöèè î íîâîì ñåíñîðå
Ðèñóíîê 11. Âûáîð ïðàâèë äëÿ ïðîñìîòðà/àêòèâàöèè $fp=popen($curl_path."curl -s $proxyline $snortrules_url | ↵ gunzip -dcf - | tar -xOf - rules/*.rules ↵ rules/*.conf rules/*.config", "r");
и $fp=popen($curl_path."curl -s $proxyline $snortrules_url ↵ 2>/dev/null | tar xzOf - rules/*.rules rules/*.conf ↵ rules/*.config", "r");
пишем $fp=popen($curl_path."curl -s $proxyline $snortrules_url | gunzip -dcf - | tar -xOf - rules/*.rules rules/*.conf rules/*.config | ↵ /var/www/html/snortcenter/convert_rules.pl", "r");
↵ ↵
и ↵ $fp=popen($curl_path."curl -s $proxyline $snortrules_url 2>/↵ ↵ dev/null | tar xzOf - rules/*.rules rules/*.conf rules/↵ *.config | /var/www/html/snortcenter/convert_rules.pl", "r");
№3(16), март 2004
После этого правила успешно загружаются, уже не выдавая сообщения об ошибке. В окошке браузера у вас появится примерно следующая картинка (см. рис. 7). Как видно, во время разгрузки правил выдаются предупреждения о неизвестности некоторых полей с комментариями: Unknown Rule option: msg:«....», однако на работоспособность snort это не влияет. После загрузки правил можно убедиться, что они оказались в БД snortcenter, для этого выбираем Resources → Rules → View Rules и наблюдаем правила (см. рис. 8). Аналогичным образом можно просмотреть переменные (Resources → Variables → View Variables), препроцессоры (Resources → Preprocessors → View Preprocessors) и другую информацию. Для просмотра не обязательно использовать средства SnortCenter, можно воспользоваться phpMyAdmin или напрямую использовать клиент mysql [4]. После того как в БД имеются правила, необходимо создать конфигурации сенсоров, активировать нужные для
55
безопасность каждого сенсора правила и закачать конфигурации на свои места, чтобы конфигурационные файлы сенсоров соответствовали записям в БД для каждого сенсора. Для того чтобы добавить сенсор в БД, выбираем Sensor Console → Add Sensor (см. рис. 9). И далее заполняем поля таблички. В имени сенсора не имеет смысла вводить знак «-», так как он автоматически после будет оттуда удалён. Среди параметров командной строки желательно указать ключ -D, чтобы snort запустился и стал демоном. В качестве пароля вводим ранее придуманный ваш_пароль№7. После того как все поля заполнены, нажимаем save (см. рис. 10). Далее необходимо для созданного сенсора активировать правила, для этого выбираем в меню Sensor Config → Rule Selection (см. рис. 11). В появившемся окне после списка внизу нажимаем на Select, после чего все правила выделяются галочками:
Можно проверить, все ли правила вы активировали или нет, для этого необходимо щёлкнуть на Rule Category Overview.
После чего появится табличка, в которой правила будут разбиты по категориям, около каждой категории будет два числа через дробь – числа активированных правил к общему числу правил в категории. Если в категории не все правила активированы, то она для удобства подсвечивается жёлтым. Те категории, в которых все правила активированы, подсвечены зелёно-салатовым (cм. рис. 12). Аналогично, даже чуть более просто активируем переменные, для этого выбираем в меню Sensor Config → Variable selection и т. д.
далее в падающем меню выбираем пункт Activate и значки около правил изменяются.
Аналогично поступаем с препроцессорами и другими пунктами меню RuleType Selection, Classificaton Selection и т. д. Единственный недостаток программы, что All означает не все правила, как может показаться, а всего лишь все правила на текущей странице, поэтому не следует забывать активировать правила на 2-й, 3-й... и т. д. страницах. Разработчики не позаботились об удобстве, поэтому нелишним будет вспомнить про наличие переменной $max_lines в файле conig.php. Однако не стоит увлекаться, уже на значении $max_lines=1000 в процессе работы с правилами может появиться следующая ошибка.
Перед тем как активировать Output Plugin Selection, необходимо создать конфигурации для каждого из сенсо-
56
безопасность ров, для этого необходимо выбрать Resources → Output Plugins → Create Output Plugin (см. рис. 13). В появившемся падающем меню следует выбрать Database (Log to a variety of databases) и нажать select (см. рис. 14). Далее появится таблица, которую необходимо заполнить (см. рис. 15). В DB Host следует указать имя или адрес сервера, на котором запущена БД, в которую следует вести логи. Аналогично заполняются и другие параметры. После заполнения необходимо нажать на save. После того как будет создан хотя бы один output plugin, его можно будет выбрать и активировать в Sensor Config → Output Plugin Selection. После того как все необходимые составляющие правил для нужного сенсора выбраны, можно создать и скинуть итого-
вый конфигурационный файл на выбранный сенсор. Для этого следует выбрать Sensor Console → View Sensors и у нужного сенсора (пока он один) нажать push (см. рис. 16). После чего снизу на жёлтом фоне появится надпись, сообщающая об успешном сбросе файла конфигурации на его место.
Казалось бы, что на этом настройка должна быть за-
Ðèñóíîê 12. Ïðîñìîòð àêòèâèðîâàííîñòè ïðàâèë ïî êàòåãîðèÿì
Ðèñóíîê 13. Âûáîð ïóíêòà ìåíþ «Create Output Plugin»
№3(16), март 2004
57
безопасность
Ðèñóíîê 14. Âûáîð ïóíêòà ìåíþ «Database (Log to a variety of databases)»
Ðèñóíîê 15. Íàñòðîéêà ïàðàìåòðîâ âåäåíèÿ ëîãîâ â ÁÄ
кончена и достаточно нажать на start, чтобы всё заработало, но, к сожалению, это не так. Сейчас мы осуществим некоторые действия, догадка о выполнении которых была получена опытным путём. Если попытаться нажать start, то мы получим сообщение об ошибке, не дающее запустить snort на сенсоре:
58
Из чего можно сделать предположение о том, что системе не хватает файла unicode.map (находится в файле с правила-
безопасность ми). По идее этот файл не должен меняться так же часто, как правила, поэтому нет смысла править SnortCenter, чтобы он умел его скидывать вместе с файлом конфигурации. Проще его скинуть один раз руками в директорию /etc/snort. Чтобы в дальнейшем не возникало подобных ошибок, лучше всего вместе с unicode.map скинуть и другие *.map-файлы, а именно для текущей версии это sid-msg.map и gen-msg.map. Далее следует подправить строчки 140-142 в файле parser.php. Вместо elseif ($what == "byte_test") { if ($rule['byte_test']) $rule['byte_test'] ↵ [$content_nr] .= "; byte_test: $val"; else $rule['byte_test'][$content_nr] = $val;
следует написать: elseif ($what == "byte_test") { $rule['byte_test'][$content_nr] = $val;
До того, как это было сделано, разборщик правил неверно разбирал правила, в результате чего дописывалось лишнее значение byte_test: и snort не хотел запускаться. Возможно, подобную операцию по корректированию в дальнейшем придётся сделать со строками 136-138, но
пока в этом нет необходимости. Также при визуальном просмотре было замечено несколько опечаток: в строчках 166, 232, 250, 251 файла db_pars.php и строчке 307 файла rules.php, скорее всего, вместо Unknown-Catagory следует написать Unknown-Category. После этого при нажатии на start snort успешно запустился, имя сенсора стало подсвеченным салатово-зелёным цветом. Аналогичным образом следует усновить Snort и SnortCenter Agent на другие машины-сенсоры и настроить их на совместную работу со SnortCenter Management Console. (Обратите внимание, у разных сенсоров переменная HOME_NET будет разная). В результате после запуска Snort на всех сенсорах у вас получится примерно следующая картинка консоли управления, где все запущенные сенсоры окажутся на зелёно-салатовом фоне (см. рис. 17). Если Snort остановлен, то подсветка будет жёлто-оранжевая, и справа вместо «stop» будет «start», а вот если сенсор недоступен, то подсветка будет красной, и также будет соответствующее сообщение (см. рис. 18). Теперь, когда всё работает, самое время поговорить об удобствах и недостатках.
Ðèñóíîê 16. Ñêèäûâàíèå êîíôèãóðàöèîííîãî ôàéëà íà ñåíñîð
Ðèñóíîê 17. Óñïåøíî ðàáîòàþùèå ñåíñîðû
№3(16), март 2004
59
безопасность
Ðèñóíîê 18. Íåêîòîðûå ñåíñîðû íåäîñòóïíû, îñòàíîâëåíû
Удобства. SnortCenter + ACID Обычно SnortCenter и ACID [10] запускаются на одном сервере, поэтому возникает логичный вопрос, почему бы не интегрировать интерфейс одного средства с другим. Конечно, этого можно и не делать, но почему бы не иметь возможность с помощью мышки из одной страницы с записями об атаках попасть путём одного щелчка на страничку состояния сенсоров. В данном случае не надо ничего придумывать, так как уже имеется готовое решение в виде SnortCenter ACID Plugin. Нам необходимо просто скачать Plugin и установить в соответствии с прилагаемыми к нему инструкциями. (Предполагается, что у пользователя уже стоит ACID и работает, если нет, то его необходимо установить, см. [4].) Для этого скачиваем файл acid0.9.6b23-plugin-v1.tar.gz: # wget http://users.pandora.be/larc/download/ ↵ acid-0.9.6b23-plugin-v1.tar.gz
Распаковываем содержимое архива в какую-нибудь директорию, например в /progi: # tar -zxvf acid-0.9.6b23-plugin-v1.tar.gz -C /progi
Переименовываем как-нибудь файлы ACID acid_main.php, acid_output_html.inc, acid_style.css, чтобы они не потерялись, так как на их место будут записаны новые. Например, допишем к ним расширения .bak: # mv /var/www/html/acid/acid_main.php /var/www/html/ ↵ acid/acid_main.php.bak # mv /var/www/html/acid/acid_output_html.inc /var/www/ ↵ html/acid/acid_output_html.inc.bak # mv /var/www/html/acid/acid_style.css /var/www/html/ ↵ acid/acid_style.css.bak
Далее копируем все файлы с учётом поддиректорий из директории /progi/acid-0.9.6b23_plugin в директорию, где установлен ACID: /var/www/html/acid: # cp -R /progi/acid-0.9.6b23_plugin/* /var/www/html/acid
60
либо с выводом имени каждого копируемого файла: # cp -Rv /progi/acid-0.9.6b23_plugin/* /var/www/html/acid
Далее необходимо подредактировать файл plugin.conf.php, изменив в нём переменную $snortcenter_home таким образом, чтобы она указывала путь к файлам snortcenter. В нашем случае вместо $snortcenter_home = «http:// localhost/»; пишем $snortcenter_home = «../snortcenter/»; Следует заметить, что помимо обычного интерфейса к ACID с полными правами мы также создавали интерфейс только для просмотра (см. [4]), соответственно, если это необходимо, все вышеописанные действия следует повторить и по отношению к нему. После установки в результате запуска ACID обычным образом мы получим следующую картинку (см. рис. 19). Следует заметить, что так как изменения не коснулись файлов snortcenter, то специальной кнопки по прямому переходу в ACID из него нет. На этом статью можно было бы и закончить, если бы не ряд недостатков, которые не хочется оставлять без внимания.
Недостатки Первый из них это то, что если произойдёт перезапуск компьютера-сенсора, на котором запущен snort, то после перезапуска работу snort, как это мы делали ранее, никто не восстановит. Как вариант необходимо вносить изменения в консоль управления SnortCenter, чтобы она умела запускать Snort автоматически и при этом отличала аварийно вставшие сенсоры от специально остановленных. Это может показаться не совсем удобным, несмотря на наличие довольно удобных средств конфигурирования и мониторинга, описанных выше. Решение этой проблемы довольно просто: необходимо, как и ранее, создать файл snortd, поместить его в директорию /etc/rc.d/init.d и сделать на него ссылки из директорий, соответствующих необходимым уровням запуска. Однако в связи с тем, что имена конфигурационных файлов теперь другие, изменится и содержание файла snortd.
безопасность Например, вместо ранее snort.conf теперь может быть snort.eth0.conf. В принципе эту проблему можно бы было решить путём создания соответствующих мягких ссылок, но сделать это невозможно, так как в некоторых случаях на одном узле может быть запущено несколько демонов snort, прослушивающих разные интерфейсы. Этот случай наиболее неудобный, но не безысходный. Первый способ решения – это правка файла ниже и дублирование строк запуска с разными конфигурациями. Однако этот путь имеет недостатки, а именно все демоны работают или не работают одновременно, как остановить или запустить лишь один из демонов? Написав более сложный скрипт, можно решить и эту проблему. Возможно, в следующих статьях я напишу этот файл и приведу его с комментариями, однако он явно понадобится не всем, да и существует более простое решение, как создание нескольких файлов запуска/останова, каждый из которых будет отвечать только за свою конфигурацию snort. В этом случае просто создаём файлы snortd2, snortd3 и т. д., аналогичные файлу snortd, вносим в них изменения и также делаем необходимые ссылки. Листинг файла snortd.(snortd2, snortd3...): #!/bin/bash # # snortd Start/Stop the snort IDS daemon. # # chkconfig: 2345 79 11 # description: snort is a lightweight network intrusion # detection tool that currently detects more than 1100 host # and network vulnerabilities, portscans, backdoors, and more. # # June 10, 2000 -- Dave Wreski <dave@linuxsecurity.com> # - initial version #
# July 08, 2000 Dave Wreski <dave@guardiandigital.com> # - added snort user/group # - support for 1.6.2 # Source function library. . /etc/rc.d/init.d/functions # Specify your network interface here INTERFACE=eth0 # See how we were called. case "$1" in start) echo -n "Starting snort: " # ifconfig eth0 up daemon /usr/local/bin/snort -o -i $INTERFACE -d -D ↵ -c /etc/snort/snort.$INTERFACE.conf touch /var/lock/subsys/snort sleep 3 if [ -f /var/log/snort/alert ] then rm /var/log/snort/alert fi echo ;; stop) echo -n "Stopping snort: " killproc snort rm -f /var/lock/subsys/snort echo ;; restart) $0 stop $0 start ;; status) status snort ;; *) echo "Usage: $0 {start|stop|restart|status}" exit 1 esac exit 0
Ðèñóíîê 19. Âèä ACID ïîñëå óñòàíîâêè SnortCenter ACID Plugin
№3(16), март 2004
61
безопасность Далее следует дать команду:
! Данные между сенсорами и консолью управления пе-
# chkconfig snortd on (# chkconfig snortd2 on)
и т. д., чтобы в директориях соответствующих уровней появились символические ссылки на этот файл. Далее следует дать команду:
! !
# chkconfig snortd on
чтобы в директориях соответствующих уровней появились символические ссылки на этот файл. После этого вышеописанная проблема запуска snort будет решена. Вторым недостатком в работе SnortCenter можно назвать слабые возможности по редактированию правил. Через веб-интерфейс нельзя внести существенные изменения в правила, а также я не заметил возможности удаления правил через веб-интерфейс. Конечно, это не критические недостатки, их можно обойти путём создания своих собственных правил (кстати, свои правила уже удаляются), их активации и деактивации ненужных, но в целом в этом направлении я бы доработал эту программу в будущем.
Положительные моменты Помимо отрицательных моментов в работе программы есть и положительные моменты, а именно хорошая «защита от дурака» при создании нерабочеспособных конфигурационных файлов. Система себя ведёт очень хитрым образом, а именно на сенсорах всегда после первого запуска хранятся две конфигурации – основная и резервная. Когда вы загружаете новый файл конфигурации, то перед его запуском система осуществляет тестирование файла конфигурации и параметров запуска с опцией «-T». В случае успешного тестирования происходит запуск Snort с новым файлом конфигурации, а резервный файл конфигурации заменяется текущим. В случае если же тестовый запуск был неуспешным, система выдаёт пользователю сообщение об ошибке и на место неправильного нового файла записывает рабочую резервную копию последнего успешного запуска. И запускает Snort «в предыдущей» конфигурации. Таким образом, даже неумелому администратору будет сложно заставить Snort не работать ошибочными правилами, если он до этого уже работал.
Заключение Описанное выше средство конфигурирования и диагностики нескольких сенсоров довольно удобно при создании сети диагностики на базе распределённых сенсоров и представляет собой относительно законченный продукт, однако в описанной выше реализации есть ряд недостатков разной степени важности, которые в будущем следует попытаться решить, если есть потребность в создании надёжной, отказоустойчивой и безопасной системы обнаружения атак. Среди недостатков можно отметить следующие: ! Передача данных с сенсоров в центральную БД в открытом виде.
62
!
!
редаются будучи закрытыми средствами слабой и непроверенной криптографии, что является защитой только от дилетантов и создаёт заведомо ложную защищённость канала. Все передаваемые в системе данные никак не защищаются от подмены. Отсутствие ведения логов в системе – кто, когда и что исправлял, например, на случай, если в системе имеется несколько администраторов. Отсутствие продуманного взаимодействия с другими средствами защиты, например, с пакетными фильтрами. По большей части написание тех или иных разрешающих/запрещающих правил на компьютере центра и сенсорах лежит на администраторе и не защищено от его ошибки. Отсутствие продуманных механизмов резервной связи на случай атак на саму систему или её части.
В целом это не единственные недостатки данной системы, а лишь основные, которые бросаются сразу в глаза. Многие из них выходят за рамки описанного выше средства, решающего только одну узкую проблему единого хранения файлов конфигурации и диагностики, и затрагивают более широкий спектр средств, обеспечивающих безопасность. Описанные выше проблемы скорее повод для их будущего более глубокого осмысления, решения и последующего описания полученных результатов на страницах журнала.
Литература, ссылки: 1. SnortCenter – Snort management console http:// users.pandora.be/larc/ 2. SnortCenter: раздел download http://users.pandora.be/ larc/download/ 3. Snort Enterprise Implemention v.2.0 prepared by Steven J. Scott, October 2002, http://users.pandora.be/larc/ documentation/snort_enterprise.pdf 4. Закляков П. Удобнее, эффективнее, лучше: snort + MySQL. – //Журнал «Системный администратор» №11(12), ноябрь 2003 г. – 62-73 с. 5. Net::SSLeay.pm Home Page http://symlabs.com/ Net_SSLeay/ 6. ACID: Installation and Configuration http:// www.andrew.cmu.edu/~rdanyliw/snort/acid_config.html 7. Матюхин М. Абстрактный доступ к БД с помощью ADODB, http://detail.phpclub.net/2003-08-19.htm 8. Getting started, Management Console Installation, http:// users.pandora.be/larc/documentation/chap1.html 9. PCRE – Perl Compatible Regular Expressions, http:// www.pcre.org/ 10. Analysis Console for Intrusion Databases, http:// www.cert.org/kb/acid/.
безопасность
ОШИБКИ ПЕРЕПОЛНЕНИЯ БУФЕРА ИЗВНЕ И ИЗНУТРИ КАК ОБОБЩЕННЫЙ ОПЫТ РЕАЛЬНЫХ АТАК
Мы живем в суровом мире. Программное обеспечение, окружающее нас, содержит дыры, многие из которых размерами со слона. В дыры лезут хакеры, вирусы и черви, совершающие набеги изо всех концов Сети. Червям противостоят антивирусы, заплатки, брандмауэры и другие умные слова, существующие лишь на бумаге и бессильные сдержать размножение червей в реальной жизни. Сеть небезопасна – это факт. Можно до потери пульса закидывать Била Гейтса тухлыми яйцами и кремовыми тортами, но ситуация от этого вряд ли изменится. Анализ показывает, что подавляющее большинство червей и хакерских атак основано на ошибках переполнения буфера, которые носят фундаментальный характер и которых не избежало практически ни одно полновесное приложение. Попытка разобраться в этой, на первый взгляд довольно скучной и незатейливой проблеме безопасности погружает вас в удивительный мир, полный приключений и интриг. Захват управления системой путем переполнения буфера – сложная инженерная задача, требующая нетривиального мышления и превосходной экипировки. Диверсионный код, заброшенный на выполнение, находится в весьма жестких и агрессивных условиях, не обеспечивающих и минимального уровня жизнедеятельности. Если вам нужен путеводитель по стране переполняющихся буферов, снабженный исчерпывающим руководством по выживанию, – эта статья для вас!
КРИС КАСПЕРСКИ 64
безопасность Чудовищная сложность современных компьютерных систем неизбежно приводит к ошибкам проектирования и реализации, многие из которых позволяют злоумышленнику захватывать контроль над удаленным узлом или делать с ним что-то нехорошее. Такие ошибки называются дырами или уязвимостями (holes и vulnerability соответственно). Мир дыр чрезвычайно многолик и разнообразен: это и отладочные люки, и слабые механизмы аутентификации, и функционально-избыточная интерпретация пользовательского ввода, и некорректная проверка аргументов и т. д. Классификация дыр чрезвычайно размыта, взаимно противоречива и затруднена (во всяком случае, своего Карла Линнея дыры еще ждут), а методики их поиска и «эксплуатации» не поддаются обобщению и требуют творческого подхода к каждому отдельному случаю. Было бы наивно надеяться, что одна-единственная публикация сможет описать весь этот зоопарк! Давайте лучше сосредоточимся на ошибках переполнения буферов как на наиболее важном, популярном, перспективном и приоритетном направлении. Большую часть статьи мы будем витать в бумажных абстракциях теоретических построений и лишь к концу спустимся на ассемблерную землю, обсуждая наиболее актуальные проблемы практических реализаций. Нет, не подумайте! Никто не собирается в сотый раз объяснять, что такое стек, адреса памяти и откуда они растут! Эта публикация рассчитана на хорошо подготовленную читательскую аудиторию, знающую ассемблер и бегло изъясняющуюся на Си/Си++ без словаря. Как происходит переполнение буфера, вы уже представляете и теперь хотели бы ознакомиться с полным списком возможностей, предоставляемых переполняющимися буферами. Какие цели может преследовать атакующий? По какому принципу происходит отбор наиболее предпочтительных объектов атаки? Другими словами, сначала мы будем долго говорить о том, что можно сделать с помощью переполнения, и лишь потом перейдем к вопросу «как именно это сделать». В любом случае эта тема заслуживает отдельной статьи. Описанные здесь приемы работоспособны на большинстве процессорных архитектур и операционных систем (например, UNIX/SPARC). Пусть вас не смущает, что приводимые примеры в основном относятся к Windows NT и производным от нее системам. Просто в момент написания статьи другой операционной системы не оказалось под рукой.
Мясной рулет ошибок переполнения, или попытка классификации Согласно «Новому словарю хакера» Эрика Раймонда ошибки переполнения буфера – это «то, что с неизбежностью происходит при попытке засунуть в буфер больше, чем тот может переварить». На самом деле, это всего лишь частный случай последовательного переполнения при записи. Помимо него существует индексное переполнение, заключающееся в доступе к произвольной ячейке памяти за концом буфера, где под «доступом» понимаются как операции чтения, так и операции записи.
№3(16), март 2004
Переполнение при записи приводит к затиранию, а следовательно, искажению одной или нескольких переменных (включая служебные переменные, внедряемые компилятором, такие, например, как адреса возврата или указатели this), нарушая тем самым нормальный ход выполнения программы и вызывая одно из следующих последствий: ! нет никаких последствий; ! программа выдает неверные данные или, попросту говоря, делает из чисел винегрет; ! программа «вылетает», зависает или аварийно завершается с сообщением об ошибке; ! программа изменяет логику своего поведения, выполняя незапланированные действия. Переполнение при чтении менее опасно, т.к. «всего лишь» приводит к потенциальной возможности доступа к конфиденциальным данным (например, паролям или идентификаторам TCP/IP-соединения). Ëèñòèíã 1. Ïðèìåð ïîñëåäîâàòåëüíîãî ïåðåïîëíåíèÿ áóôåðà ïðè çàïèñè seq_write(char *p) { char buff[8]; … strcpy(buff, p); } Ëèñòèíã 2. Ïðèìåð èíäåêñíîãî ïåðåïîëíåíèÿ áóôåðà ïðè ÷òåíèè idx_write(int i) { char buff[]="0123456789"; … return buff[i]; }
За концом буфера могут находиться данные следующих типов: другие буфера, скалярные переменные и указатели или же вовсе может не находиться ничего (например, невыделенная страница памяти). Теоретически за концом буфера может располагаться исполняемый код, но на практике такая ситуация почти никогда не встречается. Наибольшую угрозу для безопасности системы представляют именно указатели, поскольку они позволяют атакующему осуществлять запись в произвольные ячейки памяти или передавать управление по произвольным адресам, например, на начало самого переполняющегося буфера, в котором расположен машинный код, специально подготовленный злоумышленником и обычно называемый shell-кодом. Буфера, располагающиеся за концом переполняющегося буфера, могут хранить некоторую конфиденциальную информацию (например, пароли). Раскрытие чужих паролей, равно как и навязывание атакуемой программе своего пароля – вполне типичное поведение для атакующего. Скалярные переменные могут хранить индексы (и тогда они фактически приравниваются к указателям), флаги, определяющие логику поведения программы (в том числе и отладочные люки, оставленные разработчиком), и прочую информацию.
65
безопасность В зависимости от своего местоположения буфера делятся на три независимые категории: ! локальные буфера, расположенные в стеке и часто называемые автоматическими переменными; ! статичные буфера, расположенные в секции (сегменте) данных; ! динамические буфера, расположенные в куче. Все они имеют свои специфичные особенности переполнения, которые мы обязательно рассмотрим во всех подробностях, но сначала немного пофилософствуем.
Неизбежность ошибок переполнения в исторической перспективе Ошибки переполнения – это фундаментальные программистские ошибки, которые чрезвычайно трудно отслеживать и фундаментальность которых обеспечивается самой природой языка Си – наиболее популярного языка программирования всех времен и народов, – а точнее его низкоуровневым характером взаимодействия с памятью. Поддержка массивов реализована лишь частично, и работа с ними требует чрезвычайной аккуратности и внимания со стороны программиста. Средства автоматического контроля выхода за границы отсутствуют, возможность определения количества элементов массива по указателю и не ночевала, строки, завершающиеся нулем, – вообще песня… Дело даже не в том, что малейшая небрежность и забытая или некорректно реализованная проверка корректности аргументов приводит к потенциальной уязвимости программы. Корректную проверку аргументов невозможно осуществить в принципе! Рассмотрим функцию, определяющую длину переданной ей строки и посимвольно читающую эту строку до встречи с завершающим ее нулем. А если завершающего нуля на конце не окажется? Тогда функция вылетит за пределы утвержденного блока памяти и пойдет чесать непаханую целину посторонней оперативной памяти! В лучшем случае это закончится выбросом исключения. В худшем – доступом к конфиденциальным данным. Можно, конечно, передать максимальную длину строкового буфера с отдельным аргументом, но кто поручится, что она верна? Ведь этот аргумент приходится формировать вручную, и, следовательно, он не застрахован от ошибок. Короче говоря, вызываемой функции ничего не остается, как закладываться на корректность переданных ей аргументов, а раз так – о каких проверках мы вообще говорим?! С другой стороны – выделение буфера возможно лишь после вычисления длины принимаемой структуры данных, т.е. должно осуществляться динамически. Это препятствует размещению буферов в стеке, поскольку стековые буфера имеют фиксированный размер, задаваемый еще на стадии компиляции. Зато стековые буфера автоматически освобождаются при выходе из функции, снимая это бремя с плеч программиста и предотвращая потенциальные проблемы с утечками памяти. Динамические буфера, выделяемые из кучи, намного менее популярны, поскольку их использование уродует структуру программы. Если раньше обработка текущих ошибок сводилась к немедленному return, то теперь пе-
66
ред выходом из функции приходится выполнять специальный код, освобождающий все, что программист успел к этому времени понавыделять. Без критикуемого goto эта задача решается только глубоко вложенными if, обработчиками структурных исключений, макросами или внешними функциями, что захламляет листинг и служит источником многочисленных и трудноуловимых ошибок. Многие библиотечные функции (например, gets, sprintf) не имеют никаких средств ограничения длины возвращаемых данных и легко вызывают ошибки переполнения. Руководства по безопасности буквально кишат категорическими запретами на использование последних, рекомендуя их «безопасные» аналоги – fgets и snprintf, явно специфицирующие предельно допустимую длину буфера, передаваемую в специальном аргументе. Помимо неоправданного загромождения листинга посторонними аргументами и естественных проблем с их синхронизацией (при работе со сложными структурами данных, когда один-единственный буфер хранит много всякой всячины, вычисление длины оставшегося «хвоста» становится не такой уж очевидной арифметической задачей, и здесь очень легко допустить грубые ошибки) программист сталкивается с необходимостью контроля целостности обрабатываемых данных. Как минимум необходимо убедиться, что данные не были варварски обрезаны и/или усечены, а как максимум – корректно обработать ситуацию с обрезанием. А что мы, собственно, здесь можем сделать? Увеличить буфер и повторно вызывать функцию, чтобы скопировать туда остаток? Не слишком-то элегантное решение, к тому же всегда существует вероятность потерять завершающий нуль на конце. В Си++ ситуация с переполнением обстоит намного лучше, хотя проблем все равно хватает. Поддержка динамических массивов и «прозрачных» текстовых строк наконец-то появилась (и это очень хорошо), но подавляющее большинство реализаций динамических массивов работает крайне медленно, а строки тормозят еще сильнее, поэтому в критических участках кода от них лучше сразу же отказаться. Иначе и быть не может, поскольку существует только один способ построения динамических массивов переменной длины – представление их содержимого в виде ссылочной структуры (например, двунаправленного списка). Для быстрого доступа к произвольному элементу список нужно индексировать, а саму таблицу индексов где-то хранить. Таким образом, чтение/запись одного-единственненного символа выливается в десятки машинных команд и множество обращений к памяти (а память была, есть и продолжает оставаться самым узким местом, существенно снижающим общую производительность системы). Даже если компилятор вдруг решит заняться контролем границ массива (одно дополнительное обращение к памяти и три-четыре машинных команды), это не решит проблемы, поскольку при обнаружении переполнения откомпилированная программа не сможет сделать ничего умнее, чем аварийно завершить свое выполнение. Вызов исключения не предлагается, поскольку если программист забудет его обработать (а он наверняка забу-
безопасность дет это сделать), мы получим атаку типа отказ в обслуживании. Конечно, это не захват системы, но все равно нехорошо. Так что ошибки переполнения были, есть и будут! От этого никуда не уйти, и коль скоро мы обречены на длительное сосуществование с последними, будет нелишним познакомиться с ними поближе…
Окутанные желтым туманом мифов и легенд Журналисты, пишущие о компьютерной безопасности, и эксперты по безопасности, зарабатывающие на жизнь установкой этих самых систем безопасности, склонны преувеличивать значимость и могущество атак, основанных на переполнении буфера. Дескать, хакеры буфера гребут лопатой, и если не принять адекватных (и весьма дорогостоящих!) защитных мер, ваша информация превратится в пепел. Все это так (ведь и на улицу лишний раз лучше не выходить – случается, что и балконы падают), но за всю историю существования компьютерной индустрии не насчитывается и десятка случаев широкомасштабного использования переполняющихся буферов для распространения вирусов или атак. Отчасти потому, что атаки настоящих профессионалов происходят бесшумно. Отчасти – потому, что настоящих профессионалов среди современных хакеров практически совсем не осталось… Наличие одного или нескольких переполняющихся буферов еще ни о чем не говорит, и большинство ошибок переполнения не позволяет атакующему продвинуться дальше банального DoS. Вот неполный перечень ограничений, с которыми приходится сталкиваться червям и хакерам: ! строковые переполняющиеся буфера (а таковых среди них большинство) не позволяют внедрять символ нуля в середину буфера и препятствуют вводу некоторых символов, которые программа интерпретирует особым образом; ! размер переполняющихся буферов обычно оказывается катастрофически мал для вмещения в них даже простейшего загрузчика или затирания сколь-нибудь значимых структур данных; ! абсолютный адрес переполняющегося буфера атакующему чаще всего неизвестен, поэтому приходится оперировать относительными адресами, что очень непросто с технической точки зрения; ! адреса системных и библиотечных функций меняются от одной операционной системы к другой – это раз. Ни на какие адреса уязвимой программы также нельзя закладываться, поскольку они непостоянны (особенно это актуально для UNIX-приложений, компилируемых каждым пользователем самостоятельно) – а это два; ! наконец, от атакующего требуется глубокое знание команд процессора, архитектуры операционной системы, особенностей различных компиляторов языка, свободное от академических предрассудков мышление, плюс уйма свободного времени для анализа, проектирования и отладки shell-кода.
№3(16), март 2004
А теперь для контраста перечислим мифы противоположной стороны – стороны защитников информации, с какой-то детской наивностью свято верящих, что от хакеров можно защититься хотя бы в принципе: ! не существуют никаких надежных методик автоматического (или хотя бы полуавтоматического) поиска переполняющихся буферов, дающих удовлетворительный результат, и по-настоящему крупные дыры не обнаруживаются целенаправленным поиском. Их открытие – игра слепого случая; ! все разработанные методики борьбы с переполняющимися буферами снижают производительность (под час очень значительно), но не исключают возможности переполнения полностью, хотя и портят атакующему жизнь; ! межсетевые экраны отсекают лишь самых примитивнейших из червей, загружающих свой хвост через отдельное TCP/IP-соединение, отследить же передачу данных в контексте уже установленных TCP/IP-соединение никакой межсетевой экран не в силах. Существуют сотни тысяч публикаций, посвященных проблеме переполнения, краткий список которых был приведен в предыдущей статье этого цикла. Среди них есть как уникальные работы, так и откровенный «поросячий визг», подогреваемый собственной крутизной (мол, смотрите, я тоже стек сорвал! Ну и что, что в лабораторных условиях?!). Статьи теоретиков от программирования элементарно распознаются замалчиванием проблем, с которыми сразу же сталкиваешься при анализе полновесных приложений и проектировании shell-кодов (по сути своей являющихся высокоавтономными роботами). Большинство авторов ограничиваются исключительно вопросами последовательного переполнения автоматических буферов, оттесняя остальные виды переполнений на задний план, в результате чего у многих читателей создается выхолощенное представление о проблеме. На самом деле, мир переполняющихся буферов значительно шире, многограннее и интереснее, в чем мы сейчас и убедимся.
Похороненный под грудой распечаток исходного и дизассемблерного кода Как происходит поиск переполняющихся буферов и как осуществляется проектирование shell-кода? Первым делом выбирается объект нападения, роль которого играет уязвимое приложение. Если вы хотите убедиться в собственной безопасности или атаковать строго определенный узел, вы должны исследовать конкретную версию конкретного программного пакета, установленного на конкретной машине. Если же вы стремитесь прославиться на весь мир или пытаетесь сконструировать мощное оружие, дающее вам контроль над десятками тысяч, а то и миллионами машин, ваш выбор становится уже не так однозначен. С одной стороны, это должна быть широко распространенная и по возможности малоисследованная программа, исполняющаяся с наивысшими привилегиями и сидящая на портах, которые не так-то просто закрыть. Ра-
67
безопасность зумеется, с точки зрения межсетевого экрана все порты равноценны и ему абсолютно все равно, что закрывать. Однако пользователи сетевых служб и администраторы придерживаются другого мнения. Если от 135-порта, используемого червем Love San, в подавляющем большинстве случаев можно безболезненно отказаться (лично автор статьи именно так и поступил), то без услуг того же веб-сервера обойдешься едва ли. Чем сложнее исследуемое приложение, тем больше вероятность обнаружить в нем критическую ошибку. Следует также обращать внимание и на формат представления обрабатываемых данных. Чаще всего переполняющиеся буфера обнаруживаются в синтаксических анализаторах, выполняющих парсинг текстовых строк, однако большинство этих ошибок уже давно обнаружено и устранено. Лучше искать переполняющиеся буфера там, где до вас их никто не искал. Народная мудрость утверждает: хочешь что-то хорошо спрятать – положи это на самое видное место. На фоне нашумевших эпидемий Love San и Slapper с этим трудно не согласиться. Кажется невероятным, что такие очевидные переполнения до последнего времени оставались необнаруженными! Наличие исходных текстов одновременно желательно и нежелательно. Желательно – потому что они существенно упрощают и ускоряют поиск переполняющихся буферов, а нежелательно… по той же самой причине! Как говорится: больше народу – меньше кислороду. Действительно, трудно рассчитывать найти что-то новое в исходнике, зачитанном всеми до дыр. Отсутствие исходных текстов существенно ограничивает круг исследователей, отсекая многочисленную армию прикладных программистов и еще большую толпу откровенных непрофессионалов. Здесь, в прокуренной атмосфере ассемблерных команд, выживает лишь тот, кто программирует быстрее, чем думает, а думает быстрее, чем говорит. Тот, кто может удержать в голове сотни структур данных, буквально на физическом уровне ощущая их взаимосвязь и каким-то шестым чувством угадывая, в каком направлении нужно копать. Собственный программистский опыт может только приветствоваться. Во-первых, так легче вжиться в привычки, характер и образ мышления разработчика исследуемого приложения. Задумайтесь: а как бы вы решили данную задачу, окажись на его месте? Какие бы могли допустить ошибки? Где бы проявили непростительную небрежность, соблазнившись компактностью кода и элегантностью листинга? Кстати об элегантности. Бытует мнение, что неряшливый стиль программного кода неизбежно провоцирует программиста на грубые ошибки (и ошибки переполнения в том числе). Напротив, педантично причесанная программа ошибок скорее всего не содержит, и анализировать ее означает напрасно тратить свое время. Как знать… Автору приходилось сталкиваться с вопиюще небрежными листингами, которые работали как часы, потому что были сконструированы настоящими профессионалами, наперед знающими, где подстелить соломку, чтобы не упасть. Встречались и по-академически аккуратные программы, дотошно и не по одному разу проверяющие все, что только можно проверить, но букваль-
68
но нашпигованные ошибками переполнения. Тщательность сама по себе еще ни от чего не спасает. Для предотвращения ошибок нужен богатый программистский опыт, и опыт, оставленный граблями в том числе. Но вместе с опытом зачастую появляется и вальяжная небрежность – своеобразный «отходняк» от юношеского увлечения эффективностью и оптимизацией. Признаком откровенного непрофессионализма является пренебрежение #define или безграмотное использование последних. В частности, если размер буфера buff определяется через MAX_BUF_SIZE, то и размер копируемой в него строки должен ограничиваться им же, а не MAX_STR_SIZE, заданным в отдельном define. Обращайте внимание и на характер аргументов функций, работающих с блоками данных. Передача функции указателя без сообщения размера блока – частая ошибка начинающих, равно как и злоупотребление функциями strcpy/strncpy. Первая небезопасна (отсутствует возможность ограничить предельно допустимую длину копируемой строки), вторая ненадежна (отсутствует возможность оповещения о факте «обрезания» хвоста строки, не уместившегося в буфер, что само по себе может служить весьма нехилым источником ошибок). Хорошо, ошибка переполнения найдена. Что дальше? А дальше только дизассемблер. Не пытайтесь выжать из исходных текстов хоть какую-то дополнительную информацию. Порядок размещения переменных в памяти не определен и практически никогда не совпадает с порядком их объявления в программе. Может оказаться так, что большинства из этих переменных в памяти попросту нет и они размещены компилятором в регистрах либо же вовсе отброшены оптимизатором как ненужные. (Попутно заметим, что все демонстрационные листинги, приведенные в этой статье, рассчитывают, что переменные располагаются в памяти в порядке их объявления.) Впрочем, не будем забегать вперед – дизассемблирование – тема отдельной статьи, которую планируется опубликовать в следующих номерах журнала.
Цели и возможности атаки Конечная цель любой атаки – заставить систему сделать что-то «нехорошее», чего нельзя добиться легальным путем. Существует по меньшей мере четыре различных способа реализации атаки: ! чтение секретных переменных; ! модификация секретных переменных; ! передача управления на секретную функцию программы; ! передача управления на код, переданный жертве самим злоумышленником. Чтение секретных переменных. На роль секретных переменных в первую очередь претендуют пароли на вход в систему, а также пароли доступа к конфиденциальной информации. Все они так или иначе содержатся в адресном пространстве уязвимого процесса, зачастую располагаясь по фиксированным адресам (под «входом в систему» здесь подразумеваются имя пользователя и
безопасность пароль, обеспечивающие удаленное управление уязвимым приложением). Еще в адресном пространстве процесса содержатся дескрипторы секретных файлов, сокеты, идентификаторы TCP/IP-соединений и многое другое. Разумеется, вне текущего контекста они не имеют никакого смысла, но могут быть использованы кодом, переданном жертве злоумышленником, и осуществляющим, например, установку «невидимого» TCP/IP-соединения, прячась под «крышей» уже существующего. Ячейки памяти, хранящие указатели на другие ячейки, «секретными» строго говоря не являются, однако, знание их содержимого значительно облегчает атаку. В противном случае атакующему придется определять опорные адреса вслепую. Допустим, в уязвимой программе содержится следующий код: char *p = malloc(MAX_BUF_SIZE);
где p – указатель на буфер, содержащий секретный пароль. Допустим так же, что в программе имеется ошибка переполнения, позволяющая злоумышленнику читать содержимое любой ячейки адресного пространства. Весь вопрос в том: как этот буфер найти? Сканировать всю кучу целиком не только долго, но и небезопасно, т.к. можно легко натолкнуться на невыделенную страницу памяти и тогда выполнение процесса аварийно завершится. Автоматические и статические переменные в этом отношении более предсказуемы. Поэтому атакующий должен сначала прочитать содержимое указателя p, а уже затем – секретный пароль, на который он указывает. Разумеется, это всего лишь пример, которым возможности переполняющего чтения не ограничиваются. Само же переполняющее чтение реализуется по меньшей мере четырьмя следующими механизмами: «потерей» завершающего нуля в строковых буферах, модификаций указателей (см. «Указатели и индексы»), индексным переполнением (см. там же) и навязыванием функции printf (и другим функциям форматированного вывода) лишних спецификаторов. Модификация секретных переменных. Возможность модификации переменных дает значительно больше возможностей для атаки, позволяя: ! навязывать уязвимой программе «свои» пароли, дескрипторы файлов, TCP/IP-идентификаторы и т. д.; ! модифицировать переменные, управляющие ветвлением программы; ! манипулировать индексами и указателями, передавая управление по произвольному адресу (и адресу, содержащему код, специально подготовленный злоумышленником в том числе). Чаще всего модификация секретных переменных реализуется посредством последовательного переполнения буфера, по обыкновению своему поражающего целый каскад побочных эффектов. Например, если за концом переполняющегося буфера расположен указатель на некоторую переменную, в которую после переполнения что-то пишется, злоумышленник сможет затереть любую
№3(16), март 2004
ячейку памяти на свой выбор (за исключением ячеек, явно защищенных от модификации, например кодовой секции или секции .rodata, разумеется). Передача управления на секретную функцию программы. Модификация указателей на исполняемый код приводит к возможности передачи управления на любую функцию уязвимой программы (правда, с передачей аргументов имеются определенные проблемы). Практически каждая программа содержит функции, доступные только root, и предоставляющие те или иные управленческие возможности (например, создание новой учетной записи, открытие сессии удаленного управления, запуск файлов и т. д.). В более изощренных случаях управление передается на середину функции (или даже на середину машинной инструкции) с таким расчетом, чтобы процессор выполнил замысел злоумышленника, даже если разработчик программы не предусматривал ничего подобного. Передача управления обеспечивается либо за счет изменения логики выполнения программы, либо за счет подмены указателей на код. И то, и другое опирается на модификацию ячеек программы, кратко рассмотренную выше. Передача управления на код, переданный жертве самим злоумышленником, является разновидностью механизма передачи управления на секретную функцию программы, только сейчас роль этой функции выполняет код, подготовленный злоумышленником и тем или иным способом переданный на удаленный компьютер. Для этой цели может использоваться как сам переполняющийся буфер, так и любой другой буфер, доступный злоумышленнику для непосредственной модификации и в момент передачи управления на shell-код присутствующий в адресном пространстве уязвимого приложения (при этом он должен располагаться по более или менее предсказуемым адресам, иначе передавать управление будет некому и некуда).
Жертвы переполнения или объекты атаки Переполнение может затирать ячейки памяти следующих типов: указатели, скалярные переменные и буфера. Объекты языка Си++ включают в себя как указатели (указывающие на таблицу виртуальных функций, если таковые в объекте есть), так и скалярные данныечлены (если они есть). Самостоятельной сущности они не образуют и вполне укладываются в приведенную выше классификацию.
Указатели и индексы В классическом Паскале и других «правильных» языках указатели отсутствуют, но в Си/Си++ они вездесущи. Чаще всего приходится иметь дело с указателями на данные, несколько реже встречаются указатели на исполняемый код (указатели на виртуальные функции, указатели на функции, загружаемые динамической компоновкой и т. д.). Современный Паскаль (раньше ассоциируемый с компилятором Turbo Pascal, а теперь еще и DELPHI) также немыслим без указателей. Даже если в явном виде указатели и не поддерживаются, на них дер-
69
безопасность жатся динамические структуры данных (куча, разряженные массивы), используемые внутри языка. Указатели удобны. Они делают программирование простым, наглядным, эффективным и естественным. В то же время указатели во всех отношениях категорически небезопасны. Попав в руки хакера или пищеварительный тракт червя, они превращаются в оружие опустошительной мощности – своеобразный аналог BFG-900 или, по крайней мере, плазмогана. Забегая вперед, отметим, что указатели обоих типов потенциально способны к передаче управления на несанкционированный машинный код. Вот с указателей на исполняемый код мы и начнем. Рассмотрим ситуацию, когда следом за переполняющимся буфером buff, расположен указатель на функцию, которая инициализируется до и вызывается после переполнения буфера (возможно, вызывается не сразу, а спустя некоторое время). Тогда мы заполучим аналог функции call или, говоря другими словами, инструмент для передачи управления по любому (ну или почти любому) машинному адресу, в том числе и на сам переполняющийся буфер (тогда управление получит код, переданный злоумышленником). Ëèñòèíã 3. Ôðàãìåíò ïðîãðàììû, ïîäâåðæåííîé ïåðåïîëíåíèþ ñ çàòèðàíèåì óêàçàòåëÿ íà èñïîëíÿåìûé êîä code_ptr() { char buff[8]; void (*some_func) (); … printf("passws:"); gets(buff); … some_func(); }
Подробнее о выборе целевых адресов мы поговорим в другой раз, сейчас же сосредоточимся на поиске затираемых указателей. Первым в голову приходит адрес возврата из функции, находящийся внизу кадра стека (правда, чтобы до него дотянуться, требуется пересечь весь кадр целиком и не факт, что нам это удастся, к тому же его целостность контролируют многие защитные системы, подробнее о которых планируется рассказать в следующей статье). Другая популярная мишень – указатели на объекты. В Си++ программах обычно присутствует большое количество объектов, многие из которых создаются вызовом оператора new, возвращающим указатель на свежесозданный экземпляр объекта. Невиртуальные функции-члены класса вызываются точно так же, как и обычные Сифункции (т.е. по их фактическому смещению), поэтому они неподвластны атаке. Виртуальные функции-члены вызываются намного более сложным образом через цепочку следующих операций: указатель на экземпляр объекта → указатель на таблицу виртуальных функций → указатель на конкретную виртуальную функцию. Указатели на таблицу виртуальных функций не принадлежат объекту и внедряются в каждый его экземпляр, который чаще всего сохраняется в оперативной памяти, реже – в регистровых переменных. Указатели на объекты так же размещаются либо в оперативной памяти, либо в регистрах, при этом на один и тот же объект может указывать множество указателей (среди которых могут встретиться и такие, кото-
70
рые расположены непосредственно за концом переполняющегося буфера). Таблица виртуальных функций (далее просто виртуальная таблица) принадлежит не экземпляру объекта, а самому объекту, т.е. упрощенно говоря, мы имеем одну виртуальную таблицу на каждый объект. «Упрощенно» потому, что в действительности виртуальная таблица помещается в каждый obj-файл, в котором встречается обращение к членам данного объекта (раздельная компиляция дает о себе знать). И хотя линкеры в подавляющем большинстве случаев успешно отсеивают лишние виртуальные таблицы, иногда они все-таки дублируются (но это уже слишком высокие материи для начинающих). В зависимости от «характера» выбранной среды разработки и профессионализма программиста виртуальные таблицы размещаются либо в секции .data (не защищенной от записи), либо в секции .rodata (доступной лишь на чтение), причем последний случай встречается значительно чаще. Давайте для простоты рассмотрим приложения с виртуальными таблицами в секции .data. Если злоумышленнику удастся модифицировать один из элементов виртуальной таблицы, то при вызове соответствующей виртуальной функции управление получит не она, а совсем другой код! Однако добиться этого будет непросто! Виртуальные таблицы обычно размещаются в самом начале секции данных, т.е. перед статическими буферами и достаточно далеко от автоматических буферов (более конкретное расположение указать невозможно, т.к. в зависимости от операционной системы стек может находиться как ниже, так и выше секции данных). Так что последовательное переполнение здесь непригодно и приходится уповать на индексное, все еще остающееся теоретической экзотикой, робко познающей окружающий мир. Модифицировать указатель на объект и/или указатель на виртуальную таблицу намного проще, поскольку они не только находятся в области памяти, доступной для модификации, но и зачастую располагаются в непосредственной близости от переполняющихся буферов. Модификация указателя this приводит к подмене виртуальных функций объекта. Достаточно лишь найти в памяти указатель на интересующую нас функцию (или вручную сформировать его в переполняющемся буфере) и установить на него this с таким расчетом, чтобы адрес следующей вызываемой виртуальной функции попал на подложный указатель. С инженерной точки зрения это достаточно сложная операция, поскольку кроме виртуальных функций объекты еще содержат и переменные, которые более или менее активно используют. Переустановка указателя this искажает их «содержимое» и очень может быть, что уязвимая программа рухнет раньше, чем успеет вызвать подложную виртуальную функцию. Можно, конечно, сымитировать весь объект целиком, но не факт, что это удастся. Сказанное относится и к указателю на объект, поскольку с точки зрения компилятора они скорее похожи, чем различны. Однако наличие двух различных сущностей дает атакующему свободу выбора – в некоторых случаях предпочтительнее затирать указатель this, в некоторых случаях – указатель на объект.
безопасность Ëèñòèíã 4. Ôðàãìåíò ïðîãðàììû, ïîäâåðæåííîé ïîñëåäîâàòåëüíîìó ïåðåïîëíåíèþ ïðè çàïèñè, ñ çàòèðàíèåì óêàçàòåëÿ íà òàáëèöó âèðòóàëüíûõ ôóíêöèé class A{ public: virtual void f() { printf("legal\n");}; }; main() { char buff[8]; A *a = new A; printf("passwd:");gets(buff); a->f(); } Ëèñòèíã 5. Äèçàññåìáëåðíûé ëèñòèíã ïåðåïîëíÿþùèéñÿ ïðîãðàììû ñ êðàòêèìè êîììåíòàðèÿìè ; CODE XREF: start+AF↓p .text:00401000 main proc near .text:00401000 .text:00401000 var_14 = dword ptr -14h ; this .text:00401000 var_10 = dword ptr -10h ; *a .text:00401000 var_C = byte ptr -0Ch .text:00401000 var_4 = dword ptr -4 .text:00401000 .text:00401000 push ebp .text:00401001 mov ebp, esp .text:00401003 sub esp, 14h ; îòêðûâàåì êàäð ñòåêà è ðåçåðâèðóåì 14h ñòåêîâîé ïàìÿòè .text:00401003 .text:00401003 ; .text:00401006 push 4 .text:00401008 call operator new(uint) .text:0040100D add esp, 4 ; âûäåëÿåì ïàìÿòü äëÿ íîâîãî ýêçåìïëÿðà îáúåêòà A è ïîëó÷àåì ; óêàçàòåëü .text:0040100D .text:0040100D ; .text:00401010 mov [ebp+var_10], eax ; çàïèñûâàåì óêàçàòåëü íà îáúåêò â ïåðåìåííóþ var_10 .text:00401010 .text:00401010 ; .text:00401013 cmp [ebp+var_10], 0 .text:00401017 jz short loc_401026 .text:00401017 ; ïðîâåðêà óñïåøíîñòè âûäåëåíèÿ ïàìÿòè .text:00401017 ; .text:00401019 mov ecx, [ebp+var_10] .text:0040101C call A::A .text:0040101C ; âûçûâàåì êîíñòðóêòîð îáúåêòà A .text:0040101C ; .text:00401021 mov [ebp+var_14], eax ; çàíîñèì âîçâðàùåííûé óêàçàòåëü this â ïåðåìåííóþ var_14 .text:00401021 .text:00401021 ; … .text:0040102D loc_40102D: ; CODE XREF: main+24 ↑j .text:0040102D mov eax, [ebp+var_14] .text:00401030 mov [ebp+var_4], eax ; áåðåì óêàçàòåëü this è ïåðåïðÿòûâàåì åãî â ïåðåìåííóþ var_4 .text:00401030 .text:00401030 ; ; "passwd:" .text:00401033 push offset aPasswd .text:00401038 call _printf .text:0040103D add esp, 4 .text:0040103D ; âûâîäèì ïðèãëàøåíèå ê ââîäó íà ýêðàí .text:0040103D ; .text:00401040 lea ecx, [ebp+var_C] ; ïåðåïîëíÿþùèéñÿ áóôåð ðàñïîëîæåí íèæå óêàçàòåëÿ íà îáúåêò ; è ïåðâè÷íîãî óêàçàòåëÿ this, íî âûøå ïîðîæäåííîãî óêàçàòåëÿ ; this, ÷òî äåëàåò ïîñëåäíèé óÿçâèìûì .text:00401040 .text:00401040 .text:00401040 .text:00401040 ; .text:00401043 push ecx .text:00401044 call _gets .text:00401049 add esp, 4 .text:00401049 ; ÷òåíèå ñòðîêè â áóôåð .text:00401049 ; .text:0040104C mov edx, [ebp+var_4] ; çàãðóæàåì óÿçâèìûé óêàçàòåëü this â ðåãèñòð EDX .text:0040104C .text:0040104C ; .text:0040104F mov eax, [edx] .text:0040104F ; èçâëåêàåì àäðåñ âèðòóàëüíîé òàáëèöû .text:0040104F ;
№3(16), март 2004
.text:00401051 mov ecx, [ebp+var_4] .text:00401051 ; ïåðåäàåì ôóíêöèè óêàçàòåëü this .text:00401051 ; .text:00401054 call dword ptr [eax] ; âûçûâàåì âèðòóàëüíóþ ôóíêöèþ – ïåðâóþ ôóíêöèþ âèðòóàëüíîé ; òàáëèöû .text:00401054 .text:00401054 ; .text:00401056 mov esp, ebp .text:00401058 pop ebp .text:00401059 retn .text:00401059 main endp
Рассмотрим ситуацию, когда следом за переполняющимся буфером идет указатель на скалярную переменную p и сама переменная x, которая в некоторый момент выполнения программы по данному указателю и записывается (порядок чередования двух последних переменных несущественен, главное, чтобы переполняющийся буфер затирал их всех). Допустим также, что с момента переполнения ни указатель, ни переменная не претерпевают никаких изменений (или изменяются предсказуемым образом). Тогда, в зависимости от состояния ячеек, затирающих оригинальное содержимое переменных x и p, мы сможем записать любое значение x по произвольному адресу p, осуществляя это «руками» уязвимой программы. Другими словами, мы получаем аналог функций POKE и PatchByte/PatchWord языков Бейсик и IDA-Си соответственно. Вообще-то, на выбор аргументов могут быть наложены некоторые ограничения (например, функция gets не допускает символа нуля в середине строки), но это не слишком жесткое условие и имеющихся возможностей вполне достаточно для захвата управления над атакуемой системой. Ëèñòèíã 6. Ôðàãìåíò ïðîãðàììû, ïîäâåðæåííîé ïîñëåäîâàòåëüíîìó ïåðåïîëíåíèþ ïðè çàïèñè è çàòèðàíèåì ñêàëÿðíîé ïåðåìåííîé è óêàçàòåëÿ íà äàííûå, ïîãëîùàþùèìè çàòåðòóþ ïåðåìåííóþ data_ptr() { char buff[8]; int x; int *p; printf("passws:"); gets(buff); … *p = x; }
Индексы являются своеобразной разновидностью указателей. Грубо говоря, это относительные указатели, адресуемые относительно некоторой базы. Смотрите, p[i] можно представить и как *(p+i), практически полностью уравнивая p и i в правах. Модификация индексов имеет свои слабые и сильные стороны. Сильные – указатели требуют задания абсолютного адреса целевой ячейки, который обычно неизвестен, в то время как относительный вычисляется на ура. Индексы, хранящиеся в переменных типа char, лишены проблемы нулевых символов. Индексы, хранящиеся в переменных типа int, могут беспрепятственно затирать ячейки, расположенные «выше» стартового адреса (т.е. лежащие в младших адресах), при этом старшие байты индекса содержат символы FFh, которые значительно более миролюбивы, чем символы нуля. Однако, если обнаружить факт искажения указателей практически невозможно (дублировать их значение в резервных переменных не предлагать), то оценить корректность индексов перед их использованием не составляет
71
безопасность никакого труда, и многие программисты именно так и поступают (правда, «многие» еще не означает «все»). Другой слабой стороной индексов является их ограниченная «дальнобойность», составляющая ±128/256 байт (для индексов типа signed/unsigned char) и -2147483648 байт для индексов типа signed int. Ëèñòèíã 7. Ôðàãìåíò ïðîãðàììû, ïîäâåðæåííîé ïîñëåäîâàòåëüíîìó ïåðåïîëíåíèþ ïðè çàïèñè, ñ çàòèðàíèåì èíäåêñà index_ptr() {
}
char *p; char buff[MAX_BUF_SIZE]; int i; p = malloc(MAX_BUF_SIZE); i = MAX_BUF_SIZE; … printf("passws:"); gets(buff); … // if ((i < 1) || (i > MAX_BUF_SIZE)) îøèáêà while(i--) p[i] = buff[MAX_BUF_SIZE – i];
Скалярные переменные Скалярные переменные, не являющиеся ни индексами, ни указателями, намного менее интересны для атакующих, поскольку в подавляющем большинстве случаев их возможности очень даже ограничены, однако на безрыбье сгодятся и они (совместное использование скалярных переменных вместе с указателями/индексами мы только что рассмотрели, сейчас же нас интересуют скалярные переменные сами по себе). Рассмотрим случай, когда вслед за переполняющимся буфером расположена переменная buks, инициализируемая до переполнения, а после переполнения используемая для расчетов количества денег, снимаемых со счета (не обязательно счета злоумышленника). Допустим, программа тщательно проверяет входные данные и не допускает использования ввода отрицательных значений, однако, не контролирует целостность самой переменной buks. Тогда, варьируя ее содержимым по своему усмотрению, злоумышленник без труда обойдет все проверки и ограничения. Ëèñòèíã 8. Ôðàãìåíò ïðîãðàììû, ïîäâåðæåííûé ïåðåïîëíåíèþ ñ çàòèðàíèåì ñêàëÿðíîé ïåðåìåííîé var_demo(float *money_account) { char buff[MAX_BUF_SIZE]; float buks = CURRENT_BUKS_RATE; printf("input money:"); gets(buff); if (atof(buff)<0) îøèáêà! ââåäèòå ïîëîæèòåëüíîå çíà÷åíèå … *money_account -= (atof(buff) * CURRENT_BUKS_RATE); }
При всей своей искусственности приведенный пример чрезвычайно нагляден. Модификация скалярных переменных только в исключительных случаях приводит к захвату управления системой, но легко позволяет делать из чисел винегрет, а на этом уже можно сыграть! Но что же это за исключительные случаи? Во-первых, многие программы содержат отладочные переменные, оставленные разработчиками, и позволяющие, например, отключить систему аутентификации. Во-вторых, существует множество переменных, хранящих начальные или предельно допустимые значения других переменных, например, счетчиков цикла – for (a =b; a < c; a++) *p++ = *x++; очевидно, что мо-
72
дификация переменных b и c приведет к переполнению буфера p со всеми отсюда вытекающими последствиями. В-третьих… да мало ли что можно придумать – всего и не перечислишь! Затирание скалярных переменных при переполнении обычно не приводит к немедленному обрушению программы, поэтому такие ошибки могут долго оставаться не обнаруженными. Будьте внимательными!
Массивы и буфера Что интересного можно обнаружить в буферах? Прежде всего это строки, хранящиеся в PASCAL-формате, т.е. с полем длины вначале, затирание которого порождает каскад вторичных переполнений. Про уязвимость буферов с конфиденциальной информацией мы уже говорили, а теперь, пожалуйста – конкретный, хотя и несколько наигранный пример: Ëèñòèíã 9. Ôðàãìåíò ïðîãðàììû, ïîäâåðæåííîé ïîñëåäîâàòåëüíîìó ïåðåïîëíåíèþ ïðè çàïèñè ñ çàòèðàíèåì ïîñòîðîííåãî áóôåðà buff_demo() { char buff[MAX_BUF_SIZE]; char pswd[MAX_BUF_SIZE]; … fgets(pswd, MAX_BUF_SIZE, f); … printf("passwd:"); gets(buff); if (strncmp(buff, pwsd, MAX_BUF_SIZE)) // íåïðàâèëüíûé ïàðîëü else // ïðàâèëüíûé ïàðîëü }
Еще интересны буфера, содержащие имена открываемых файлов (можно заставить приложение записать конфиденциальные данные в общедоступный файл или, напротив, навязать общедоступный файл взамен конфиденциального), тем более что несколько подряд идущих буферов, вообще говоря, не редкость.
Заключение Изначально статья задумывалась как исчерпывающее руководство, снабженное большим количеством листингов и избегающее углубляться в академические теоретизирования. Теперь, вычитывая статью перед заключительной правкой и внося в нее мелкие, косметические улучшения, я с грустью осознаю, что выполнить свой замысел мне так и не удалось… До практических советов разговор вообще не дошел, и львиная часть подготовленного материала осталась за кадром. Ох, и не следовало мне пытаться объять необъятное… Надеюсь, что следующие статьи этого цикла исправят положение. В первую очередь планируется рассказать о передовых методиках переполнения кучи, приводящих к захвату управления удаленной машиной, обсудить технические аспекты разработки shell-кода (приемы создания позиционно-независимого кода, проблема вызова системных функций, оптимизация размера и т. д.), затем можно будет перейти к разговору о способах поиска переполняющихся буферов и о возможных мерах по заблаговременному предотвращению оных. Отдельную статью хотелось бы посвятить целиком червям Love Sun и Slapper, поскольку их дизассемблерные листинги содержат очень много интересного.
hardware
РЕАЛИЗАЦИЯ НИЗКОУРОВНЕВОЙ ПОДДЕРЖКИ ШИНЫ PCI В ЯДРЕ ОПЕРАЦИОННОЙ СИСТЕМЫ LINUX
В данной статье на примере решения простой задачи – определения MAC-адреса сетевой карты – рассмотрена реализация низкоуровневой поддержки (low-level support) шины PCI в ядре операционной системы Linux.
ВЛАДИМИР МЕШКОВ 74
hardware Постановка задачи и исходные данные Исходные данные – имеется компьютер, функционирующий под управлением ОС Linux, версия ядра 2.4.24. В PCIслот установлен сетевой адаптер на чипсете RTL8139C (далее – адаптер RTL8139C). Задача – определить MAC-адрес этого адаптера. Путей решения этой задачи несколько. Можно воспользоваться командами dmesg или ifconfig: root@bob~/# dmesg | grep eth0 eth0: RealTek RTL8139 at 0xc000, 00:02:44:72:5e:4e, IRQ 11 eth0: Identified 8139 chip type 'RTL-8100B/8139D' root@bob~/# ifconfig | grep eth0 eth0 Link encap:Ethernet HWaddr 00:02:44:72:5E:4E
Можно написать небольшое приложение следующего вида: /* get_mac.c */ #include <stdio.h> #include <sys/socket.h> #include <sys/ioctl.h> #include <linux/if.h> int main() { int fd; struct ifreq ifr; unsigned char mac[6]; fd=socket(AF_INET,SOCK_DGRAM,0); memset(&ifr,0,sizeof(struct ifreq)); memcpy(ifr.ifr_name,"eth0",4); ioctl(fd,SIOCGIFHWADDR,&ifr); memcpy(mac,(char *)&(ifr.ifr_hwaddr.sa_data), ↵ sizeof(struct sockaddr)); printf("%.2x:%.2x:%.2x:%.2x:%.2x:%.2x\n", mac[0],mac[1],mac[2],mac[3],mac[4],mac[5]); }
return 0;
Можно извлечь MAC-адрес из самого адаптера RTL8139C. Рассмотрим, как это делается. Согласно спецификации на сетевой адаптер RTL8139C, MAC-адрес занимает первые 6 байт в пространстве портов ввода/вывода (I/O), отведенного адаптеру. Задавая смещение относительно базового порта I/O, можно прочитать все 6 байт MAC-адреса. Пример функции, выполняющей процедуру чтения MAC-адреса, приведен ниже: void get_mac_addr(u32 base_addr) { u8 mac[6]; /* * Ïîñëåäîâàòåëüíî ÷èòàåì áàéòû èç ïîðòà base_addr * è ñîõðàíÿåì èõ â ìàññèâå mac[] */ for(int i = 0; i < 6; i++) mac[i] = inb(base_addr + i); /* * Îòîáðàæàåì ðåçóëüòàò */ printf("%02X:%02X:%02X:%02X:%02X:%02X\n", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); }
Функция get_mac_addr() принимает в качестве параметра адрес порта I/O адаптера RTL8139C и в цикле производит считывание данных MAC-адреса из пространства
№3(16), март 2004
I/O, выделенного адаптеру. При выходе из цикла в буфере mac[] будет находиться искомый MAC-адрес. Теперь вся задача сводится к определению значения базового адреса порта I/O адаптера RTL8139C. Как найти этот адрес? Чтобы ответить на этот вопрос, давайте познакомимся поближе с шиной PCI.
Общая характеристика шины PCI Разработка шины PCI началась весной 1991 года как внутренний проект корпорации Intel (Release 0.1). Специалисты компании поставили перед собой цель разработать недорогое решение, которое бы позволило полностью реализовать возможности нового поколения процессоров 486/ Pentium/P6. Особенно подчеркивалось, что разработка проводилась «с нуля», а не была попыткой установки новых «заплат» на существующие решения. В результате шина PCI появилась в июне 1992 года (R1.0). Разработчики Intel отказались от использования шины процессора и ввели еще одну «антресольную» (mez-zanine) шину. Благодаря такому решению шина получилась, во-первых, процессорно-независимой, а во-вторых, могла работать параллельно с шиной процессора, не обращаясь к ней за запросами и тем самым снижая её загрузку. Стандарт шины был объявлен открытым и передан PCI Special Interest Group (www.pcisig.com), которая продолжила работу по совершенствованию шины. В настоящее время действует спецификация PCI версии 2.3. Основные возможности шины следующие: ! Синхронный 32- или 64-разрядный обмен данными. При этом для уменьшения числа контактов (и стоимости) используется мультиплексирование, то есть адрес и данные передаются по одним и тем же линиям. ! Поддержка 5V и 3.3V логики. Частота 66 МГц поддерживается только 3.3V логикой. ! Частота работы шины 33 МГц или 66 МГц позволяет обеспечить широкий диапазон пропускных способностей (с использованием пакетного режима): ! 132 Мб/сек при 32-бит/33 МГц; ! 264 Mб/сек при 32-бит/66 МГц; ! 264 Mб/сек при 64-бит/33 МГц; ! 528 Мб/сек при 64-бит/66 МГц.
! ! ! ! !
При этом для работы шины на частоте 66 МГц необходимо, чтобы все периферийные устройства работали на этой частоте. Полная поддержка multiply bus master (например, несколько контроллеров жестких дисков могут одновременно работать на шине). Автоматическое конфигурирование карт расширения при включении питания. Спецификация шины позволяет комбинировать до восьми функций на одной карте (например, видео + звук). Шина позволяет устанавливать до 4 слотов расширения, однако возможно использование моста PCI-PCI для увеличения количества карт расширения. PCI-устройства оборудованы таймером, который используется для определения максимального промежутка времени, в течение которого устройство может занимать шину.
75
hardware Шина поддерживает метод передачи данных, называемый «linear burst» (метод линейных пакетов). Этот метод предполагает, что пакет информации считывается (или записывается) «одним куском», то есть адрес автоматически увеличивается для следующего байта, при этом увеличивается скорость передачи данных за счет уменьшения числа передаваемых адресов. На одной шине PCI может присутствовать несколько устройств, каждое из которых имеет свой номер (device number). В системе может присутствовать несколько шин PCI, каждая из которых имеет свой номер (PCI bus number). Шины нумеруются последовательно; шина, подключённая к главному мосту, имеет нулевой номер. В каждой транзакции (обмене по шине) участвуют два устройства – инициатор (initiator) обмена, он же мастер (master) или ведущее устройство, и целевое (target) устройство, оно же ведомое (slave). Шина PCI все транзакции трактует как пакетные: каждая транзакция начинается фазой адреса, за которой может следовать одна или несколько фаз данных.
Конфигурационное пространство устройства PCI Согласно спецификации, каждое устройство PCI имеет конфигурационное пространство (configuration space) размером 256 байт, в котором содержится информация о самом устройстве и о ресурсах, занимаемых устройством. Это пространство не приписано ни к пространству памяти, ни к пространству ввода-вывода. Доступ к нему осуществляется по специальным циклам шины Configuration Read и Configuration Write. После аппаратного сброса (или по включении питания) устройства PCI доступны только для операций конфигурационного чтения и записи. В этих операциях устройства выбираются по индивидуальным сигналам IDSEL и сообщают о потребностях в ресурсах и возможных вариантах конфигурирования. После распределения ресурсов, выполняемого программой конфигурирования (во время теста POST), в конфигурационные регистры устройства записываются параметры конфигурирования. Только после этого к устройству становится возможным доступ по командам обращения к памяти и портам ввода-вывода. Для того чтобы всегда можно было найти работоспособную конфигурацию, все ресурсы, занимаемые картой, должны быть перемещаемыми в своих пространствах. Для многофункциональных устройств каждая функция должна иметь свое конфигурационное пространство. Конфигурационное пространство, формат которого представлен на рис. 1, состоит из трех областей: ! область, не зависимая от устройства (device-independent header region); ! область, определяемая типом устройства (header-type region); ! область, определяемая пользователем (user-defined region). Подробное описание полей конфигурационного пространства приведено в спецификации [4]. Рассмотрим кратко основные поля.
76
Ðèñóíîê 1. Ôîðìàò êîíôèãóðàöèîííîãî ïðîñòðàíñòâà PCI
Vendor_ID, Device_ID, Class_Code Поля Vendor_ID, Device_ID и Class_Code содержат код фирмы-изготовителя устройства, код устройства и код класса устройства. Классификация устройств и указание кода класса в его конфигурационном пространстве является важной частью спецификации PCI. Код изготовителя, код устройства и код класса применяются в процессе поиска заданного устройства. Если необходимо найти конкретное устройство, то поиск выполняется по кодам устройства и его изготовителя; если необходимо найти все устройства определенного типа, то поиск выполняется по коду класса устройства. После того как устройство найдено, при помощи регистров базовых адресов можно определить выделенные ему области в адресном пространстве памяти и пространстве вводавывода (I/O).
Header_Type Поле Header_Type определяет формат header-type области, а также является ли устройство многофункциональным. Идентификатором многофункционального устройства является бит 7 поля: если бит установлен в 1 – устройство поддерживает несколько функций, если сброшен в 0 – устройство выполняет только функцию. Биты 0 – 6 определяют собственно формат header-type области: если эти биты обнулены (содержат код 0x00), то формат области header-type соответствует формату, представленому на рис. 1; значение 0x01 идентифицирует устройство как мост PCI-to-PCI, и формат header-type области
hardware описан в спецификации PCI-to-PCI Bridge Architecture Specification; значение 0x02 идентифицирует устройство как мост CardBus. Остальные значения зарезервированы.
Command Командный регистр (поле Command) содержит средства управления устройством. Назначение отдельных бит этого регистра: ! бит 0 – определяет реакцию устройства на обращение к нему через пространство портов I/O. Если бит сброшен в 0, устройство игнорирует попытки доступа к нему через порты I/O; ! бит 1 – определяет реакцию устройства на обращение к нему через адресное пространство. Если бит установлен в 1, устройство отвечает на обращения к нему через адресное пространство; если бит сброшен в 0, то устройство на попытки доступа к нему не реагирует; ! бит 2 – установленный в 1 бит разрешает устройству работать в режиме Bus Master.
Base Address Registers Регистры базовых адресов (Base Address Registers) содержат выделенные устройству области в адресном пространстве и пространстве портов I/O. Бит 0 во всех регистрах базовых адресов определяет, куда будет отображен ресурс – на пространство портов I/O или на адресное пространство. Регистр базового адреса, отображаемый на пространство портов, всегда 32-разрядный, бит 0 установлен в 1. Регистр базового адреса, отображаемый на адресное пространство, может быть 32- и 64-разрядным, бит 0 сброшен в 0.
Программный доступ к конфигурационному пространству PCI Configuration Mechanism #1 Поскольку конфигурационное пространство не имеет привязки к какой-либо определенной области адресного пространства, для доступа к нему применяется специальный механизм, названый в спецификации Configuration Mechanism #1. Для работы этого механизма в пространстве портов I/O зарезервированы два 32-разрядных порта, входящих в главный мост: CONFIG_ADDRESS с адресом 0xCF8 и CONFIG_DATA с адресом 0xCFC. Формат CONFIG_ADDRESS представлен на рис. 2.
Ðèñóíîê 2. Ôîðìàò ðåãèñòðà CONFIG_ADDRESS
Установленный в 1 бит 31 разрешает обращение к конфигурационному пространству через порт CONFIG_DATA, биты 30 – 24 зарезервированы (read-only), при чтении должны возвращать 0, биты 23 – 26 содержат номер шины, биты 15 – 11 – номер устройства, биты 10 – 8 – номер функции и биты 7 – 2 – номер регистра, к которому выполняется обращение (смещение в конфигурационном пространстве).
№3(16), март 2004
Порядок работы Configuration Mechanism #1 следующий – в порт CONFIG_ADDRESS (0xCF8) заносится адрес, соответствующий формату, приведенному на рис. 2; обращением к порту CONFIG_DATA (0xCFC) производится чтение или запись данных в требуемый регистр конфигурационного пространства.
PCI BIOS Для взаимодействия с устройствами PCI имеются дополнительные функции BIOS, доступные как из реального, так и защищенного режима работы процессора. Эти функции предназначены для работы с конфигурационным пространством и генерации специальных циклов PCI. Функции PCI BIOS для 16-битного реального режима вызываются через прерывание int 0x1A. Номер функции задается в регистре AX. Признаком нормального выполнения являются значения флага CF = 0 и ноль в регистре AH (AH = 0x00, SUCCESFUL). Если CF = 1, то регистр AH содержит код ошибки: ! 0x81 – неподдерживаемая функция (FUNC_NOT_SUPPORTED); ! 0x83 – неправильный идентификатор производителя (BAD_VENDOR_ID); ! 0x86 – устройство не найдено (DEVICE_NOT_FOUND); ! 0x87 – неправильный номер регистра PCI (BAD_REGISTER_NUMBER), т.е. неправильно задано смещение в конфигурационном пространстве. Перечислим некоторые функции PCI BIOS (полный перечень содержится в [6]): ! 0xB101 – проверка присутствия PCI BIOS; ! 0xB102 – поиск устройства по коду фирмы-изготовителя; ! 0xB103 – поиск устройства по коду класса; ! 0xB108 – чтение байта конфигурационного пространства устройства PCI; ! 0xB109 – чтение слова конфигурационного пространства устройства PCI; ! 0xB10A – чтение двойного слова конфигурационного пространства устройства PCI. При чтении информации из конфигурационного пространства в регистры процессора заносятся следующие значения: ! AX – номер функции; ! BH – номер шины, к которой подключено устройство (от 0 до 255); ! BL – номер устройства в старших 5 битах и номер функции в трех младших; ! DI – смещение в конфигурационном пространстве. После этого следует вызов прерывания int 0x1A, в результате которого в регистрах процессора будут размещены следующие значения: ! ECX – считанная информация (байт/слово/двойное слово); ! AH – код возврата (SUCCESFUL/BAD_REGISTER_NUMBER); ! CF – статус возврата (0 – функция успешно выполнена, 1 – ошибка).
77
hardware BIOS32 При работе в 32-разрядном защищенном режиме для доступа к функциям PCI BIOS используются средства BIOS32. Процедура проверки наличия BIOS32 предполагает обращение к физическим адресам памяти, поэтому обычно производится из реального режима, до переключения в защищенный. Если BIOS32 поддерживается, то в области памяти BIOS, расположенной в диапазоне 0xE0000 – 0xFFFFF, должна присутствовать специальная 16-байтная структура данных – служебный каталог (BIOS32 Service Directory). Структура каталога приведена в таблице 1. Òàáëèöà 1
Процесс поиска служебного каталога BIOS32 заключается в сканировании памяти ПЗУ BIOS: производится поиск сигнатуры «_32_» в диапазоне 0xE0000 – 0xFFFFF по 16-байтным параграфам (начало служебного каталога выровнено на границу 16 байт). После обнаружения сигнатуры производится вычисление и проверка контрольной суммы: если сумма совпадает, то служебный каталог найден. Для доступа к PCI BIOS в 32-разрядном режиме требуется выполнить дальний вызов через точку входа BIOS32. Перед выполнением вызова в регистры записывается следующая информация: ! в EAX – идентификатор запрашиваемого сервиса, который для PCI BIOS имеет значение «$PCI» (0x49435024); ! в EBX – селектор функции (значение должно быть равно нулю). После выполнения вызова в регистре AL будет возвращен код результата: ! 0 – операция успешно завершена; ! 0x80 – некорректный идентификатор сервиса; ! 0x81 – недопустимое значение селектора функции. Если вызов завершен успешно, в регистрах процессора будет размещена следующая информация: ! в EBX – физический адрес базы сервиса BIOS; ! в ECX – размер сегмента сервиса BIOS; ! в EDX – точка входа в сервис BIOS (смещение относительно базы, возвращенной в EBX). Адрес точки входа в сервис определяется путем сложения физического адреса базы сервиса и смещения относительно базы. Дальнейший порядок обращения к функциям сервиса PCI BIOS не отличается от реального режима, за исключением того, что вместо вызова прерывания int 0x1A необходимо выполнить дальний вызов через точку входа в сервис.
Алгоритм чтения MAC-адреса Итак, вернемся к решению нашей задачи – определению MAC-адреса сетевого адаптера RTL8139C. Для этого нам
78
был необходим базовый адрес в пространстве I/O, и это значение, как мы уже установили, находится в конфигурационном пространстве устройства. Чтобы извлечь его оттуда, можно воспользоваться сервисом BIOS32 либо работать с устройством напрямую, при помощи Configuration Mechanism #1. Алгоритм решения задачи при использовании сервиса BIOS32 следующий: ! определяем адрес точки входа в BIOS32. Для этого выполняем поиск служебного каталога BIOS32 в диапазоне 0xE0000 – 0xFFFFF; ! определяем адрес точки в сервис BIOS32 – PCI BIOS. Для этого выполняем дальний вызов через точку входа в BIOS32, задав в регистре EAX идентификатор запрашиваемого сервиса ($PCI); ! используя PCI BIOS, выполняем поиск сетевого адаптера и определяем его координаты – номер шины, номер устройства и номер функции. Поиск производим по коду класса. Код класса сетевого контроллера равен 0x00020000 (см. [4]); ! из конфигурационного пространства сетевого адаптера считываем значение базового адреса порта I/O и, задавая смещение относительно этого адреса, считываем MAC-адрес адаптера RTL8139C. При использовании Configuration Mechanism #1 всё гораздо проще и сводится к последовательной записи информации в порт CONFIG_ADDRESS и чтении её из порта CONFIG_DATA. Рассмотрим программную реализацию этих алгоритмов.
Программная реализация алгоритма чтения MAC-адреса Разработаем модуль ядра, который при загрузке будет выполнять поиск сетевого адаптера RTL8139C и считывать его MAC-адрес. Все исходные тексты, рассмотренные в статье, доступны на сайте журнала. Начнём с описания заголовочных файлов, переменных и информационных структур. Заголовочных файлов у нас два: #include <linux/module.h> #include <linux/pci.h>
Определим код фирмы-изготовителя адаптера RTL8139C, код типа устройства и код класса устройства. // êîä ôèðìû-èçãîòîâèòåëÿ – RealTek #define VENDOR_ID 0x10EC // êîä óñòðîéñòâà – ñåòåâàÿ êàðòà RTL8139C #define DEVICE_ID 0x8139 // êîä êëàññà ñåòåâîãî êîíòðîëëåðà ([4]) #define CLASS_CODE 0x00020000
Функции PCI BIOS, которые мы будем использовать (полный перечень приведен в [6]): // ïðîâåðêà ïðèñóòñòâèÿ PCI BIOS â ñèñòåìå #define PCIBIOS_PCI_BIOS_PRESENT 0xb101 // ïîèñê óñòðîéñòâà PCI çàäàííîãî òèïà #define PCIBIOS_FIND_PCI_DEVICE 0xb102 // ïîèñê óñòðîéñòâà PCI çàäàííîãî êëàññà #define PCIBIOS_FIND_PCI_CLASS_CODE 0xb103 // ïðî÷èòàòü áàéò èç êîíôèãóðàöèîííîãî ïðîñòðàíñòâà
hardware // óñòðîéñòâà PCI #define PCIBIOS_READ_CONFIG_BYTE 0xb108 // ïðî÷èòàòü ñëîâî èç êîíôèãóðàöèîííîãî ïðîñòðàíñòâà // óñòðîéñòâà PCI #define PCIBIOS_READ_CONFIG_WORD 0xb109 // ïðî÷èòàòü äâîéíîå ñëîâî èç êîíôèãóðàöèîííîãî ïðîñòðàíñòâà // óñòðîéñòâà PCI #define PCIBIOS_READ_CONFIG_DWORD 0xb10a
Сигнатура, по которой производится поиск служебного каталога BIOS32 (_32_): #define BIOS32_SIGNATURE (('_' << 0) + ('3' << 8) + ↵ ('2' << 16) + ('_' << 24))
Сигнатура для проверки присутствия PCI BIOS в системе (используется функцией PCIBIOS_PCI_BIOS_PRESENT): #define PCI_SIGNATURE (('P' << 0) + ('C' << 8) + ↵ ('I' << 16) + (' ' << 24))
Сигнатура, по которой осуществляется поиск сервиса BIOS32: #define PCI_SERVICE (('$' << 0) + ('P' << 8) + ↵ ('C' << 16) + ('I' << 24))
Определим структуру для хранения информации об устройстве PCI: struct pci_dev_struct { // êîä ôèðìû-èçãîòîâèòåëÿ è êîä òèïà óñòðîéñòâà u16 vendor_id, device_id; // êîä êëàññà óñòðîéñòâà u32 class_code; // àäðåñ ïîðòà I/O u32 base_addr; // êîîðäèíàòû óñòðîéñòâà – íîìåð øèíû, íîìåð óñòðîéñòâà // íà øèíå è íîìåð ôóíêöèè óñòðîéñòâà u8 bus, dev, fn; };
Следующие две структуры описывают физические адреса точек входа в BIOS32 и в сервис BIOS32 (PCI BIOS). Физический адрес точки входа в BIOS32 (в формате селектор:смещение): static struct { u32 address; u16 segment; } bios32_indirect = { 0, __KERNEL_CS };
Физический адрес точки входа в сервис BIOS32 (PCI BIOS): static struct { u32 address; u16 segment; } pci_indirect = { 0, __KERNEL_CS };
__KERNEL_CS – селектор сегмента кода, определен в файле include/asm-i386/segment.h: #define __KERNEL_CS 0x10
Стандартный служебный каталог BIOS32 имеет следующий вид: union bios32 { struct { // ñèãíàòóðà _32_ u32 signature;
№3(16), март 2004
};
// 32-õ áèòíûé ôèçè÷åñêèé àäðåñ òî÷êè âõîäà â BIOS32 u32 entry; // íîìåð âåðñèè, Revision level, 0 u8 revision; // ðàçìåð ñëóæåáíîãî êàòàëîãà â 16-áàéòíûõ ïàðàãðàôàõ u8 length; // êîíòðîëüíàÿ ñóììà, äîïîëíÿåò âñå áàéòû äî 0 u8 checksum; // çàðåçåðâèðîâàíî, çàïîëíÿåòñÿ íóëÿìè u8 reserved[5]; } fields; char chars[16];
Рассмотрим функцию, которая производит поиск служебного заголовка BIOS32. int pci_find_bios(void) { union bios32 *check; // ñëóæåáíûé êàòàëîã BIOS32 u8 sum; int i, length;
Сканируем область памяти BIOS в диапазоне адресов 0xe0000 и 0xfffff в поисках сигнатуры «_32_» и служебного каталога BIOS32: for (check = (union bios32 *) __va(0xe0000); check <= (union bios32 *) __va(0xffff0); ++check) { if (check->fields.signature != BIOS32_SIGNATURE) continue;
Поиск выполняется относительно нижней границы адресного пространства ядра 0xC0000000, на что указывает макрос __va(0xe0000) и __va(0xffff0). Этот макрос определен в файле include/page.h следующим образом: // íèæíÿÿ ãðàíèöà àäðåñíîãî ïðîñòðàíñòâà ÿäðà #define __PAGE_OFFSET 0xC0000000 #define PAGE_OFFSET ((unsigned long)__PAGE_OFFSET) #define __va(x) ((void *)((unsigned long)(x) + PAGE_OFFSET))
Если сигнатура найдена – определяем размер служебного каталога BIOS32 в байтах: length = check->fields.length * 16; if (!length) continue;
Считываем контрольную сумму и определяем номер версии реализации: sum = 0; for (i = 0; i < length ; ++i) sum += check->chars[i]; if (sum != 0) continue; if (check->fields.revision != 0) { printk("PCI: unsupported BIOS32 revision %d ↵ at 0x%p\n", check->fields.revision, check); continue; }
Если вышли за пределы диапазона сканирования, то использовать BIOS32 мы не сможем: if (check->fields.entry >= 0x100000) { printk("PCI: BIOS32 entry (0x%p) in high memory, ↵ cannot use.\n", check); return 0; } else {
Если всё в порядке – вычисляем адрес точки входа в BIOS32 и заполняем структуру bios32_indirect:
79
hardware unsigned long bios32_entry = check->fields.entry; bios32_indirect.address = bios32_entry + ↵ PAGE_OFFSET; // àäðåñ òî÷êè âõîäà â BIOS32 printk(KERN_INFO "PCI: BIOS32 entry point at ↵ 0x%08x\n", bios32_indirect.address);
} break; /* Hopefully more than one BIOS32 cannot happen... */
}
} return 0;
Проанализируем код возврата: switch (return_code) {
Если код возврата равен 0, то сервиc PCI BIOS присутствует, и адрес точки входа в него можно определить, сложив значение адреса базы (address) и смещения относительно базы (entry):
Следующая функция, которую мы рассмотрим, определяет адрес точки входа в сервис BIOS32. static u32 bios32_service(u32 service) { u8 return_code; /* %al, êîä âîçâðàòà */ u32 address; /* %ebx, àäðåñ áàçû ñåðâèñà*/ u32 length; /* %ecx, ðàçìåð ñåãìåíòà ñåðâèñà u32 entry; /* %edx, òî÷êà âõîäà â ñåðâèñ */ u32 flags;
*/
Идентификатор сервиса передается в параметрах функции. Адрес точки входа в сервис BIOS32 определяется путем дальнего вызова через точку входа в BIOS32. Перед выполнением вызова в регистры процессора заносится следующая информация: ! EAX – идентификатор сервиса (в нашем случае это $PCI); ! EBX – селектор функции (должен быть равен 0); ! EDI – адрес точки входа в BIOS32. После вызова регистры процессора будут содержать следующую информацию: ! AL – код возврата: 0 – запрашиваемый сервис найден, 0x80 – сервис отсутствует (не поддерживается); ! EBX – физический адрес базы сервиса; ! ECX – размер сегмента сервиса; ! EDX – точка входа в сервис BIOS32 (смещение относительно базы, возвращённой в регистре EBX). Вот как выполняется данный вызов: __save_flags(flags); __cli(); __asm__("lcall (%%edi); cld" : "=a" (return_code), // ôèçè÷åñêèé àäðåñ áàçû ñåðâèñà "=b" (address), // ðàçìåð ñåãìåíòà ñåðâèñà "=c" (length), // òî÷êà âõîäà â ñåðâèñ BIOS32 (ñìåùåíèå îòíîñèòåëüíî // áàçû, âîçâðàùåííîé â EBX) "=d" (entry) // èäåíòèôèêàòîð çàïðàøèâàåìîãî ñåðâèñà ($PCI) : "0" (service), // ñåëåêòîð ôóíêöèè, äîëæåí áûòü ðàâåí íóëþ "1" (0), // àäðåñ òî÷êè âõîäà â BIOS32 "D" (&bios32_indirect)); __restore_flags(flags);
Конструкция типа __save_flags(flags); __cli(); /* This code runs with interrupts disabled */ __restore_flags(flags);
используется для защиты критичных участков кода от воздействия прерываний.
80
case 0: // èñêîìûé àäðåñ òî÷êè âõîäà â ñåðâèñ BIOS32 (PCI BIOS) return address + entry;
Если код возврата равен 0x80, запрашиваемый сервис отсутствует:
}
}
case 0x80: /* Not present */ printk(KERN_WARNING "bios32_service(0x%08x): ↵ not present\n", service); return 0; default: /* Shouldn't happen */ printk(KERN_WARNING "bios32_service(0x%08x): returned ↵ 0x%x -- BIOS bug!\n", service, return_code); return 0;
А теперь рассмотрим порядок обращения к сервису PCI BIOS в защищенном режиме, выполнив проверку присутствия PCI BIOS в системе: int check_pcibios(void) { u32 signature, eax, ebx, ecx; u8 status, major_ver, minor_ver, hw_mech; u32 flags, u32 pcibios_entry;
pcibios_entry – это точка входа в сервис, назначение остальных переменных рассмотрим ниже. Ищем точку входа в сервис PCI путём вызова функции bios32_service(). Параметр функции – идентификатор сервиса, сигнатура $PCI. Все адреса отсчитываются относительно нижней границы адресного пространства ядра (PAGE_OFFSET = 0xC0000000). if ((pcibios_entry = bios32_service(PCI_SERVICE))) { pci_indirect.address = pcibios_entry + PAGE_OFFSET;
Выполняем обращение к функции проверки присутствия PCI BIOS в системе – 0xB101. Для этого выполняем дальний вызов через точку входа pcibios_entry, предварительно заполнив регистры процессора следующей информацией: ! EAX – запрашиваемая функция сервиса, в данном случае 0xB101; ! EDI – адрес точки входа в сервис. В результате выполнения вызова в регистрах процессора будет находиться следующая информация: ! EDX – сигнатура запрашиваемого сервиса (PCI в нашем случае); ! AH – признак присутствия сервиса (0 – PCI BIOS присутствует, если в EDX правильная сигнатура, любое другое значение – PCI BIOS отсутствует); ! AL – поддерживаемый аппаратный механизм конфигурирования (см. «Configuration Mechanism #1»); ! BH – номер версии интерфейса;
hardware ! BL – подномер версии интерфейса; ! CL – номер последней шины PCI в системе. Итак, выполняем вызов: __save_flags(flags); __cli(); __asm__( "lcall (%%edi); cld\n\t" "jc 1f\n\t" "xor %%ah, %%ah\n" "1:" // â EDX âîçâðàùàåòñÿ ñèãíàòóðà "PCI" : "=d" (signature), // â AH – ïðèçíàê ïðèñóòñòâèÿ, â AL – àïïàðàòíûé ìåõàíèçì "=a" (eax), // â BH - íîìåð âåðñèè èíòåðôåéñà PCI, BL – ïîäíîìåð // âåðñèè èíòåðôåéñà "=b" (ebx), // ECX – íîìåð ïîñëåäíåé øèíû PCI â ñèñòåìå "=c" (ecx) // 0xB101 – ôóíêöèÿ ïðîâåðêè ïðèñóòñòâèÿ PCI BIOS // â ñèñòåìå : "1" (PCIBIOS_PCI_BIOS_PRESENT), // òî÷êà âõîäà â ñåðâèñ BIOS32 "D" (&pci_indirect) : "memory"); __restore_flags(flags);
и обрабатываем полученные результаты:
шине и номер функции), передаются по ссылке и будут изменены на реальные значения. Для поиска устройства выполняем дальний вызов через точку входа в сервис BIOS32, задав в регистрах процессора соответствующие параметры: ! EAX – запрашиваемая функция сервиса, в данном случае 0xB102. ! ECX – код типа устройства. ! EDX – код фирмы-изготовителя устройства. ! ESI – индекс (порядковый номер) устройства заданного типа. ! В регистр EDI занесем адрес точки входа в сервис. В результате выполнения вызова в регистрах процессора будет находиться следующая информация: ! BH – номер шины, к которой подключено устройство; ! BL – номер устройства в старших пяти битах и номер функции в трёх младших; ! AH – код возврата (может принимать значения BAD_VENDOR_ID, DEVICE_NOT_FOUND и SUCCESFUL). Выполняем дальний вызов:
// ïðèçíàê ïðèñóòñòâèÿ ñåðâèñà ñ ñèñòåìå status = (eax >> 8) & 0xff; // ïîääåðæèâàåìûé àïïàðàòíûé ìåõàíèçì hw_mech = eax & 0xff; // íîìåð âåðñèè major_ver = (ebx >> 8) & 0xff; // íîìåð ïîäâåðñèè minor_ver = ebx & 0xff;
__asm__("lcall (%%edi); cld\n\t" "jc 1f\n\t" "xor %%ah, %%ah\n" "1:" : "=b" (bx), "=a" (ret) : "1" (PCIBIOS_FIND_PCI_DEVICE), "c" (device_id), "d" (vendor), "S" ((int) index), // àäðåñ òî÷êè âõîäà â ñåðâèñ "D" (&pci_indirect));
Если сервис присутствует, переменная status будет равна 0. Проверяем это, а заодно и полученную сигнатуру: if (status || signature != PCI_SIGNATURE) { printk (KERN_ERR "PCI: BIOS BUG #%x[%08x] ↵ found\n", status, signature); return 0; } printk(KERN_INFO "PCI: PCI BIOS revision %x.%02x entry ↵ at 0x%08x\n", major_ver, minor_ver, pcibios_entry); return 1;
}
} return 0;
Функция pci_bios_find_device() выполняет поиск устройства заданного типа при помощи PCI BIOS и возвращает его координаты – номер шины, к которой подключено устройство, номер устройства на шине и номер функции устройства: static int pci_bios_find_device(u16 vendor, u16 device_id, ↵ u16 index, u8 *bus, u8 *dev, u8 *fn) { u16 bx; u16 ret;
Функция принимает следующие параметры:
! vendor – код фирмы-изготовителя устройства PCI; ! device_id – код типа устройства; ! index – порядковый номер устройства заданного типа.
Обрабатываем полученный результат:
}
*bus = (bx >> 8) & 0xff; // íîìåð øèíû *dev = (bx & 0xff) >> 3; // íîìåð óñòðîéñòâà íà øèíå *fn = bx & 0x3; // íîìåð ôóíêöèè return (int) (ret & 0xff00) >> 8;
Функция поиска устройства заданного класса pci_bios_find_class() практически не отличается от функции поиска устройства по типу: static int pci_bios_find_class(u32 class_code, u16 index, ↵ struct pci_dev_struct *pd) { u16 bx; u16 ret;
В параметрах функции передается указатель на информационную структуру struct pci_dev_struct *pd. Перед выполнением дальнего вызова в регистры заносятся следующие данные: ! EAX – запрашиваемая функция сервиса – 0xB103. ! ECX – код класса устройства. ! ESI – индекс (порядковый номер) устройства заданного типа. ! В регистр EDI занесем адрес точки входа в сервис.
Если устройство одно, то его порядковый номер равен 0. Параметры bus, dev и fn, соответствующие координатам устройства PCI (номер шины, номер устройства на
№3(16), март 2004
Результаты выполнения вызова аналогичны предыдущим: ! в регистре BH – номер шины;
81
hardware ! в BL – номер устройства в старших пяти битах и номер !
функции в трёх младших; в AH – код возврата (DEVICE_NOT_FOUND или SUCCESFUL):
После выполнения функции в регистре ECX будут находиться считанные данные, а регистр AH будет содержать код возврата. Подготовим значение для загрузки в регистр BX и прочитаем информацию из конфигурационного пространства устройства, учитывая размер запрашиваемых данных:
__asm__("lcall (%%edi); cld\n\t" "jc 1f\n\t" "xor %%ah, %%ah\n" "1:" : "=b" (bx), "=a" (ret) : "1" (PCIBIOS_FIND_PCI_CLASS_CODE), "c" (class_code), "S" ((int) index), // àäðåñ òî÷êè âõîäà â ñåðâèñ "D" (&pci_indirect));
bx = ((bus << 8) | (dev << 3) | fn); switch (len) { case 1: // ñ÷èòûâàåì áàéò __asm__("lcall (%%esi); cld\n\t" "jc 1f\n\t" "xor %%ah, %%ah\n" "1:" : "=c" (*value), "=a" (result) : "1" (PCIBIOS_READ_CONFIG_BYTE), "b" (bx), "D" ((long)reg), // òî÷êà âõîäà â ñåðâèñ "S" (&pci_indirect)); break;
Заносим в структру struct pci_dev_struct *pd координаты устройства:
}
// íîìåð øèíû pd->bus = (bx >> 8) & 0xff; // íîìåð óñòðîéñòâà íà øèíå pd->dev = (bx & 0xff) >> 3; // íîìåð ôóíêöèè pd->fn = bx & 0x03; return (int) (ret & 0xff00) >> 8;
case 2: // ñ÷èòûâàåì ñëîâî __asm__("lcall (%%esi); cld\n\t" "jc 1f\n\t" "xor %%ah, %%ah\n" "1:" : "=c" (*value), "=a" (result) : "1" (PCIBIOS_READ_CONFIG_WORD), "b" (bx), "D" ((long)reg), // òî÷êà âõîäà â ñåðâèñ "S" (&pci_indirect)); break;
После того как устройство заданного типа найдено, необходимо получить данные, находящиеся в его конфигурационном пространстве (см. «Конфигурационное пространство устройства PCI»). Сделаем это при помощи функции pci_bios_read(): static int pci_bios_read(int bus, int dev, int fn, int reg, ↵ int len, u32 *value) { u32 result = 0; u32 bx;
Параметрами функции являются координаты устройства (bus – номер шины, dev – номер устройства, fn – номер функции), смещение в конфигурационном пространстве (reg) и размер данных для считывания (len, байт/слово/двойное слово). В последний параметр мы поместим считанное из конфигурационного пространства значение, поэтому этот параметр передается по ссылке. Проверяем правильность переданных параметров: if (bus > 255 || dev > 31 || fn > 7 || reg > 255) return -EINVAL;
Для чтения информации из конфигурационного пространства устройства PCI BIOS предоставляет следующие функции [6]: ! 0xB108 – чтение байта; ! 0xB109 – чтение слова; ! 0xB10A – чтение двойного слова. Эти функции отличаются только размером считываемых данных – байт, слово или двойное слово. Перед вызовом функции в регистры процессора помещается следующая информация: ! EAX – код функции; ! BH – номер шины, к которой подключено устройство; ! BL – номер устройства в старших пяти битах и номер функции в трёх младших битах; ! DI – смещение в конфигурационном пространстве.
82
}
case 4: // ñ÷èòûâàåì äâîéíîå ñëîâî __asm__("lcall (%%esi); cld\n\t" "jc 1f\n\t" "xor %%ah, %%ah\n" "1:" : "=c" (*value), "=a" (result) : "1" (PCIBIOS_READ_CONFIG_DWORD), "b" (bx), "D" ((long)reg), // òî÷êà âõîäà â ñåðâèñ "S" (&pci_indirect)); break; } return (int)((result & 0xff00) >> 8);
Помимо средств BIOS32 для работы с конфигурационным пространством устройства PCI в защищенном режиме также используется Configuration Mechanism #1. Его порядок работы был рассмотрен в «Configuration Mechanism #1»: в порт CONFIG_ADDRESS (0xCF8) заносится адрес, соответствующий формату, приведенному на рис. 2; обращением к порту CONFIG_DATA (0xCFC) производится чтение или запись данных в требуемый регистр конфигурационного пространства. Формировать адрес установленного формата будет макрос PCI_CONF1_ADDRESS(): #define PCI_CONF1_ADDRESS(bus, dev, fn, reg) | ↵ (0x80000000 | (bus << 16) | (dev << 11) | ↵ (fn << 8) | (reg & ~3))
Самый старший бит установлен в 1 – это позволит нам получить данные из порта CONFIG_DATA. После того как адрес сформирован, записываем его в порт CONFIG_ADDRESS. Функция pci_direct_read() выполняет обращение к устройству PCI при помощи Configuration Mechanism #1:
hardware static int pci_direct_read(int bus, int dev, int fn, ↵ int reg, int len, u32 *value) {
Параметры функции – номер шины bus, номер устройства на шине dev, номер функции fn, смещение в конфигурационном пространстве reg, длина запрашиваемых данных (байт/слово/двойное слово). Результат помещается в параметр value, который передается по ссылке. Проверяем правильность переданных параметров:
for(dev = 0; dev < 32; dev++) { // ñêàíèðóåì âñå ôóíêöèè for(fn = 0; fn < 8; fn++) {
Считываем двойное слово, находящееся по смещению idx, и получаем код класса: pci_direct_read(bus, dev, fn, idx, 4, &config_dword); code = config_dword >> 8;
Сравниваем полученный код класса с искомым. При совпадении сохраняем координаты устройства
if (bus > 255 || dev > 31 || fn > 7 || reg > 255) return -EINVAL
if(code == class_code) {
Формируем адрес при помощи макроса PCI_CONF1_ ADDRESS() и записываем его в порт CONFIG_ADDRESS:
printk(KERN_INFO "OK. Device found.\n"); printk(KERN_INFO "bus - %d, dev - %d, ↵ fn - %d\n", bus, dev, fn);
outl(PCI_CONF1_ADDRESS(bus, dev, fn, reg), 0xCF8);
pd->bus = bus; pd->dev = dev; pd->fn = fn;
Считываем значение из порта CONFIG_DATA:
}
switch (len) { case 1: // ñ÷èòûâàåì áàéò *value = inb(0xCFC + (reg & 3)); break; case 2: // ñ÷èòûâàåì ñëîâî *value = inw(0xCFC + (reg & 2)); break; case 4: // ñ÷èòûâàåì äâîéíîå ñëîâî *value = inl(0xCFC); break; } return 0;
Функция pci_direct_find_class() выполняет поиск устройства заданного класса, используя Configuration Mechanism #1, и возвращает его координаты – номер шины, номер устройства на шине и номер функции: int pci_direct_find_class(u32 class_code, ↵ struct pci_dev_struct *pd) {
Параметры функции – код класса устройства и структура struct pci_dev_struct *pd, в которую необходимо записать координаты устройства. int bus, dev, fn = 0, idx = 0x08; u32 config_dword, code;
Переменная idx – это смещение в конфигурационном пространстве устройства, и указывает оно на поле Revision ID, за которым следуют три байта поля Class Code. Для считывания Class Code достаточно считать двойное слово, находящееся по смещению idx, и сдвинуть результат на 8 бит в сторону младших разрядов. Для поиска устройства по коду класса необходимо просканировать все шины, все устройства на каждой шине и все функции устройства – до тех пор, пока не найдем устройство соответствующего класса (или пока не закончатся шины). С этой целью организуем цикл: printk(KERN_INFO "Looking for device with class code ↵ 0x%X\n", class_code); memset(pd, 0, sizeof(struct pci_dev_struct)); // ñêàíèðóåì âñå øèíû for(bus = 0; bus < 256; bus++) { // ñêàíèðóåì âñå óñòðîéñòâà íà êàæäîé øèíå
№3(16), март 2004
}
}
}
}
return 0;
} return 0x80;
Все функции, которые мы рассмотрели, будут вызваны во время процедуры инициализации модуля: static int __init pcidev_on(void) { // ñòðóêòóðà ñ ïàðàìåòðàìè PCI-óñòðîéñòâà struct pci_dev_struct pdev; // ñìåùåíèå ê äàííûì â êîíôèãóðàöèîííîì ïðîñòðàíñòâå // óñòðîéñòâà PCI int idx = 0; // êîîðäèíàòû óñòðîéñòâà u8 bus = 0, dev = 0, fn = 0; // êîìàíäíûé ðåãèñòð u16 command_reg = 0; u32 config_dword = 0;
Напомню, что наша задача – прочитать MAC-адрес сетевого адаптера RTL8139C, и для этого нам необходимо получить его базовый адрес в пространстве I/O. Ищем служебный заголовок BIOS32, вычисляем адрес точки входа в BIOS32 и производим проверку присутствия PCI BIOS: pci_find_bios(); check_pcibios();
Выполняем поиск устройства (сетевого адаптера RTL8139C) по коду типа устройства. В результате мы получим его координаты – номер шины, номер устройства на шине и номер функции: if(pci_bios_find_device(VENDOR_ID, DEVICE_ID, idx, ↵ &bus, &dev, &fn) == PCIBIOS_SUCCESSFUL) printk(KERN_INFO "Device found by type, bus - %d, ↵ dev - %d, fn - %d\n", bus, dev, fn);
Повторим процедуру, но в этот раз будем искать устройство по коду класса: memset((void *)&pdev, 0, sizeof(struct pci_dev_struct)); if(pci_bios_find_class(CLASS_CODE, idx, ↵ &pdev) == PCIBIOS_SUCCESSFUL) printk(KERN_INFO "Device found by class, bus - %d, ↵ dev - %d, fn - %d\n", pdev.bus, pdev.dev, pdev.fn); else {
83
hardware }
printk(KERN_INFO "Device not founf by class\n"); return 0;
Итак, устройство найдено. Считываем из конфигурационного пространства код фирмы-производителя и заносим это значение в структуру struct pci_dev_struct pdev:
}
return;
void get_mac_addr(u32 base_addr) { int i = 0; u8 mac[6]; memset(mac, 0, 6);
/* Read VENDOR ID */ idx = 0x00; if(pci_bios_read(pdev.bus, pdev.dev, pdev.fn, idx, ↵ 2, &config_dword) == PCIBIOS_SUCCESSFUL) pdev.vendor_id = (u16)config_dword;
/* Get and display MAC address */ for(; i < 6; i++) mac[i] = inb(base_addr + i); printk(KERN_INFO "MAC address: ↵ %02X:%02X:%02X:%02X:%02X:%02X\n", mac[0], mac[1], ↵ mac[2], mac[3], mac[4], mac[5]);
То же самое – для кода типа устройства и для кода класса устройства: /* Read DEVICE ID */ idx = 0x02; if(pci_bios_read(pdev.bus, pdev.dev, pdev.fn, idx, ↵ 2, &config_dword) == PCIBIOS_SUCCESSFUL) pdev.device_id = (u16)config_dword; /* Read Class Code */ idx = 0x08; if(pci_bios_read(pdev.bus, pdev.dev, pdev.fn, idx, ↵ 4, &config_dword) == PCIBIOS_SUCCESSFUL) pdev.class_code = config_dword >> 8;
Считываем значение командного регистра: /* Read Command Register */ idx = 0x04; if(pci_bios_read(pdev.bus, pdev.dev, pdev.fn, idx, ↵ 1, &config_dword) == PCIBIOS_SUCCESSFUL) command_reg = config_dword;
Считываем значение базового адреса в пространстве I/O. Предварительно проверяем, чтобы бит 0 командного регистра был установлен в единицу. Если это так, то выполняем поиск базового адреса устройства: /* Read Base Address Registers */ idx = 0x10; if(command_reg & 0x01) { // ñêàíèðóåì Base Address Registers â ïîèñêàõ àäðåñà // ïîðòà I/O for(; idx < 0x28 ;) { if(pci_bios_read(pdev.bus, pdev.dev, pdev.fn, ↵ idx, 4, &config_dword) == PCIBIOS_SUCCESSFUL) { // åñëè íóëåâîé áèò ðàâåí 1, òî àäðåñ ïîðòà // I/O íàéäåí if(config_dword & 0x01) { config_dword &= ~0x1; pdev.base_addr = config_dword; break; } idx += 4; } } } else return 0;
Базовый адрес найден. Отобразим информацию об устройстве и прочитаем MAC-адрес адаптера RTL8139C: display_pcidev_info(&pdev); get_mac_addr(pdev.base_addr);
Функции display_pcidev_info() и get_mac_addr() выглядят следующим образом: void display_pcidev_info(struct pci_dev_struct *pdev) { printk(KERN_INFO "VENDOR ID - 0x%X\n", pdev->vendor_id); printk(KERN_INFO "DEVICE ID - 0x%X\n", pdev->device_id); printk(KERN_INFO "CLASS CODE - 0x%X\n", pdev->class_code); printk(KERN_INFO "BASE ADDRESS - 0x%X\n", pdev->base_addr);
84
}
return;
Теперь давайте выполним процедуру чтения MAC-адреса сетевого адаптера RTL8139C, используя Configuration Mechanism #1 для доступа к конфигурационному пространству устройства. Пытаемся найти устройство по коду класса: /* Direct read PCI */ printk(KERN_INFO "PCI direct access:\n"); if(pci_direct_find_class(CLASS_CODE, &pdev) < 0) { printk(KERN_INFO "Device not found\n"); return 0; }
Считываем код фирмы-производителя, код типа устройства и код класса устройства: /* Read VENDOR_ID */ idx = 0x00; if(pci_direct_read(pdev.bus, pdev.dev, pdev.fn, idx, ↵ 2, &config_dword) == 0) printk(KERN_INFO "VENDOR_ID - 0x%X\n", config_dword); /* Read DEVICE_ID */ idx = 0x02; if(pci_direct_read(pdev.bus, pdev.dev, pdev.fn, idx, ↵ 2, &config_dword) == 0) printk(KERN_INFO "DEVICE_ID - 0x%X\n", config_dword); /* Read Class Code */ idx = 0x08; if(pci_direct_read(pdev.bus, pdev.dev, pdev.fn, idx, ↵ 4, &config_dword) == 0) printk(KERN_INFO "CLASS_CODE - 0x%X\n", ↵ config_dword >> 8);
Считываем содержимое командного регистра и значение адреса порта I/O: /* Read Command Register */ command_reg = 0; idx = 0x04; if(pci_direct_read(pdev.bus, pdev.dev, pdev.fn, idx, ↵ 2, &config_dword) == 0) command_reg = config_dword; /* Read Base Address Registers */ idx = 0x10; if(command_reg & 0x01) { for(; idx < 0x28 ;) { if(pci_direct_read(pdev.bus, pdev.dev, pdev.fn, ↵ idx, 4, &config_dword) == PCIBIOS_SUCCESSFUL) { if(config_dword & 0x01) { config_dword &= ~0x1; pdev.base_addr = config_dword; break; } idx += 4; } }
hardware } else return 0;
Считываем значение MAC-адреса сетевого адаптера.
}
get_mac_addr(config_dword); return 0;
Выгружает модуль из памяти функция pcidev_off: static void __exit pcidev_off(void) { return; }
Инициализация модуля и выгрузка его из памяти выполняется при помощи двух макросов: module_init(pcidev_on); module_exit(pcidev_off);
Исходные тексты модуля доступны на сайте журнала и находятся в файле pcidev.c. При помощи команды make получаем объектный модуль pcidev.o и загружаем его командой insmod: insmod pcidev.o
Вся информация, полученная от устройства, будет собрана в файле /var/log/messages.
Пример записи из этого файла сравните с результатами, полученными при помощи команд dmesg и ifconfig (см. «Постановка задачи и исходные данные»).
Заключение Рассмотренные нами функции являются базовыми в подсистеме низкоуровневой поддержки (low-level support) шины PCI ядра ОС Linux. Все эти функции можно найти в файле arch/i386/kernel/pci-pc.c. В повседневной практике нет особой необходимости работать напрямую с шиной, для этих целей целесообразно применять функции более высокого уровня, перечень которых приведён в файле Documentation/pci.txt. Насчет спецификаций и где их брать – спецификация на RTL8139C находится на сайте компании RealTek, www.realtek.com.tw, спецификация PCI 3.0 и перевод на русский язык спецификации PCI 2.0 были найдены на сайте http://dsp.neora.ru. На сайте Intel (www.intel.com) можно взять спецификацию на сетевые карты Intel 8255x – для этого в строке поиска задайте 8255X_OpenSDM (OpenSDM – Open Source Software Developer Manual). Также посетите сайт фирмы Phoenix (www.phoenix.com) – материалы по BIOS. Список кодов классов и подклассов устройств PCI находится в [4], приложение D.
Литература: 1. Аппаратные средства IBM PC. Энциклопедия, 2-е изд. / М. Гук – СПб.: Питер, 2003. – 923 с.:ил. 2. Программирование на аппаратном уровне: специальный справочник. 2-е изд. / В. Кулаков. – СПб.: Питер, 2003. – 848 с.:ил. 3. Шина PCI (Peripheral Component Interconnect bus). Николай Дорофеев, www.ixbt.com. 4. PCI Local Bus Specification. Revision 3.0. August 12, 2002. 5. Standard BIOS 32-bit Service Directory Proposal, Revision 0.4 May 24, 1993 6. PCI BIOS specification. Revision 2.0. 1993.
№3(16), март 2004
85
образование
ПРОГРАММНОЕ УПРАВЛЕНИЕ ADSI: LDAP
В предыдущих статьях [1, 2] были рассмотрены теоретические аспекты построения Active Directory и проведен обзор доступных провайдеров, с помощью которых можно программно управлять Active Directory, а также описаны основы программирования одного из провайдеров – WinNT. Данный материал содержит основы программирования провайдера LDAP, объектная модель которого рассмотрена на примере стандартных утилит, созданных компанией Microsoft.
ИВАН КОРОБКО
88
образование
Объектная модель провайдера LDAP Для программного управления Active Directory с помощью провайдера LDAP необходимо использовать его объектную модель. Объектная модель представляет собой совокупность объектов, которые взаимосвязаны друг с другом и образуют между собой иерархическую структуру. Каждый из этих объектов имеет набор свойств, характерных исключительно для объектов данного типа. Существует несколько типов (идентификаторов) объектов: CN, DС, OU. Расшифровка и назначение каждого объекта см. в таблице 1. Òàáëèöà 1
OU=OU_Name_Level1/OU=OU_Name_Level2…/OU=OU_Name_Levelµ представляют собой вложенные друг в друга элементы. В развернутой форме доступа объект CN является «дном колодца» в иерархии. Пример: запрос к объекту CN=User3 с помощью развернутой формы доступа выглядит следующим образом (см. рис. 1): Set obj = GetObject ("LDAP://DC=RU/DC=Domain1/OU=Group1/ ↵ OU=Group4/CN=User3")
Сокращенная форма Форма характеризуется тем, что строка запроса строится в соответствии с обратной иерархией структуры организации. Шаблон выглядит следующим образом: Set obj=GetObject("LDAP://CN=CN_Name,OU=OU_Name_Levelµ…, ↵ OU=OU_Name_Level2,OU=OU_Name_Level1/DC=…")
Имена LDAP URL Имена LDAP URL (см. RFC 1779, RFC 2247) построены на основе протокола X.500 и используются для связывания с объектами. Идентификаторы объектов DC, OU, CN образуют полное составное имя (Distinguished Name, DN), а имя самого объекта – относительное составное имя (Relative Distinguished Name, RDN). Полное составное имя объекта включает в себя имя объекта и всех его родителей, начиная с корня домена (см. рис. 1).
Соответственно запрос к объекту CN=User3 с помощью сокращенной формы доступа выглядит следующим образом: Set obj=GetObject("LDAP://CN=User3,OU=Group4, ↵ OU=Group3,DC=Domain1,dc=RU")
Инструменты, обеспечивающие доступ к объектной модели каталога Существует несколько программ, предназначенных для просмотра объектной модели каталога. Остановимся лишь на двух из них: Active Directory Viewer (Microsoft) и LDAP Browser 2.5.3 (Softerra).
Active Directory Viewer (Microsoft)
Ðèñóíîê 1
Существуют две формы доступа к ADSI: развернутая и сокращенная. Рассмотрим принципы построения путей к ресурсу двумя способами на примере домена domain.com (см. рис. 1).
Развернутая форма При использовании этого вида формы строка связывания начинается с описания верхнего элемента структуры. Затем происходит переход вниз по иерархии. Важно помнить, что при написании пути к объекту необходимо исключать пробелы. В качестве шаблона может служить выражение вида:
Active Directory Viewer (ADV) является графической утилитой, позволяющей выполнять операции чтения, модифицирования, осуществлять поиск в любых совместимых каталогах, таких как Active Directory, Exchange Server, Netscape Directory, Netware Directory. Active Directory Viewer входит в состав SDK для Active Directory Services Interface, который можно бесплатно загрузить с сайта Microsoft: http://www.microsoft.com/ntserver/ nts/downloads/other/adsi25. После установки SDK for ADSI утилита Active Directory Viewer (AdsVw.exe) будет находиться в c:\Program Files\ Microsoft\ADSI Resource Kit, Samples and Utilities\ADsVw\. Программа работает в двух режимах: ObjectViewer и Query (см. рис. 2). Для просмотра объектной модели какого-либо провайдера необходимо использовать режим ObjectViewer. Режим Query используется для осуществления поиска объектов в выбранной объектной модели. В данной статье режим Query рассматриваться не будет.
Set obj = GetObject ("LDAP://DC=Domain_name1/ ↵ DC=Domain_name2/DC=Domain_name3/OU=OU_Name_Level1/ ↵ OU=OU_Name_Level2…/OU=OU_Name_Levelµ/CN=CN_Name")
где DC=Domain_name1/DC=Domain_name2/DC=Domain_name3 образуют полное имя контроллера домена, элементы
№3(16), март 2004
Ðèñóíîê 2
89
образование Просмотр и редактирование объектной модели программой ADV в режиме ObjectViewer После выбора режима работы ObjectViewer появится диалоговое окно (см. рис. 3). Для получения доступа к каталогу необходимо указать путь к каталогу и параметры учетной записи, обладающей правами администратора (имя и пароль). Путь к каталогу должен быть построен в соответствии со следующим шаблоном:
Обратите внимание на две особенности в этом шаблоне: в шаблоне не должно быть пробелов, «слэши» должны быть прямыми – «/». Невыполнение хотя бы одного из перечисленных условий приведет к ошибке в соединении с каталогом. Для доступа к серверу server домена domain.ru с помощью протокола LDAP используется следующий запрос: LDAP://server/DC=domain,DC=ru;
<Provider_Name:>//<Server_Name>/<Full_Domain_Name>
После соединения с каталогом на экране будет отображена его иерархическая структура (см. рис. 4). В левой части экрана отображается иерархическая структура каталога. В правой части отображаются характеристики объекта, на котором установлен курсор. Список свойств объекта и соответствующих им значений приведен в «Properties» и «Property Value». С помощью кнопок «Change», «Clear», «Append», «Delete» можно изменять объектную модель каталога: изменять, удалять, добавлять поля в свойствах объектов.
LDAP Browser 2.5.3 (Softerra)
Ðèñóíîê 3
Ðèñóíîê 4
90
LDAP Browser 2.5.3 является бесплатной программой (http:// www.ldapadministrator.com). По своим возможностям программа превосходит Active Directory Viewer, в использовании LDAP Browser гораздо удобнее. В процессе создания соединения с каталогом могут быть заданы фильтры, параметры административ-
образование ной учетной записи, порт TCP, по которому имеет место соединение, и другие параметры. Общий вид программы приведен на рис. 5.
Различия в функционале провайдеров LDAP и WinNT Об одном из отличий речь велась в предыдущей статье [2] – провайдер LDAP (Lightweight Directory Access Protocol) рассматривает принтер как сетевое устройство, в то время как провайдер WinNT рассматривает принтер исключительно как локальное устройство. Использование обоих провайдеров при работе с принтерами позволяет полностью управлять принтерами. Наглядный пример управления принтерами домена рассмотрен в статье «Управление сетевыми принтерами домена» [3]. Вторым принципиальным отличием провайдеров являются расширенные возможности поиска провайдера LDAP. Используя провайдер WinNT, можно было осуществлять поиск, пользуясь фильтром, который позволял осуществлять поиск всех объектов, принадлежащих к одному из классов – computer, user, service и др. Провайдер позволяет искать объект, при этом не обязательно указывать класс, к которому относится объект. Найдя объект, осуществляем чтение его свойств, включая местоположение объекта в AD, класс, к которому относится объект, и другие параметры. Поиск объектов осуществляется с помощью OLE Distributed Query (DB) интерфейса, который вызывается прямо из интерфейса службы активного каталога (Active Directory Service Interface – ADSI). Поиск осуществляется на основании запроса и его параметров. В качестве параметров запроса могут быть: уровень поиска, диапазон поиска, ограни-
чение по размеру, сортировка и т. д. Форма запроса (Distributed Query) заимствована из Microsoft SQL Server. Запрос строится по шаблону: SELECT ïîëå11,ïîëå21,ïîëån1 FROM ïóòü ↵ WHERE objectClass="òèï_îáúåêòà"
где путь – путь к интересующему объекту AD в формате LDAP URL. На практике поиск объектов осуществляется следующим образом: Ïðèìåð 1: Set objNameSpace = GetObject("WinNT:") For Each Domain in objNameSpace DomainName=Domain.Name Next Set objConnection = CreateObject("ADODB.Connection") Set objCommand = CreateObject("ADODB.Command") objConnection.Provider = "ADsDSOObject" objConnection.Open "Active Directory Provider" Set objCommand.ActiveConnection = objConnection objCommand.CommandText = "SELECT printerName, serverName ↵ FROM " _ & " 'LDAP://"& DomainName & "' ↵ WHERE objectClass='printQueue'" objCommand.Properties("Cache Results") = False Set objRecordSet = objCommand.Execute objRecordSet.MoveFirst Do Until objRecordSet.EOF temp=temp & "Printer Name: " ↵ & objRecordSet.Fields("printerName").Value ↵ & " Server Name: " ↵ & objRecordSet.Fields("serverName").Value & chr(13) objRecordSet.MoveNext Loop wscript.echo temp
В приведенном примере осуществляется поиск всех зарегистрированных в AD-принтеров. У найденных прин-
Ðèñóíîê 5
№3(16), март 2004
91
образование теров происходит чтение двух полей: название принтера и название сервера печати. Поиск объектов с помощью провайдера LDAP осуществляется по следующему шаблону: ! устанавливается соединение с Active Directory Provider через ADODB; ! составляется запрос, на основе которого будет осуществляться поиск; ! осуществляется поиск по заданным критериям. В том случае если искомые объекты найдены, то осуществляется чтение указанных в запросе полей. Результат выводится на экран. В качестве объектов может быть строка или массив. Следует отметить, что вместо названия свойства, которое необходимо прочитать, можно указать порядковый номер поля, под которым оно обозначено в запросе. Поля отсчитываются с нуля. Таким образом, основываясь на приведенном примере, вместо objRecordSet.Fields(«serverName»).Value можно записать objRecordSet.Fields(1).Value. Третье отличие – это управление коммерческими продуктами, поддерживающими LDAP. Множество коммерческих продуктов использует каталог LDAP для хранения информации. Используя ADSI, можно управлять коммерческими продуктами, в число которых входят: Microsoft Windows 2000, 2003; Microsoft Exchange 5.5, 2000, 2003; Microsoft Site Server 3.0 + SP2; Netscape Directory Server и др. Кроме того, компании Cisco и Microsoft предложили стандарт сети на основе каталога, в котором описывается интеграция сетевых устройств в каталоге LDAP. В настоящей статье будет рассмотрен вопрос, касающийся управления Microsoft Windows 200х, а именно Active Directory. Рассмотрим следующие вопросы, касающиеся управления AD через провайдер LDAP: просмотр атрибутов записи от анонимной и конкретной учетной записи; изменение атрибутов учетной записи; создание и удаление учетной записи.
Просмотр атрибутов записи от анонимной и конкретной учетной записи Просмотр атрибутов объектов осуществляется с помощью функции Get(), вызову которой предшествует вызов функции GetObject(). Для получения анонимного доступа к объектам необходимо указать путь к объекту, начиная с контроллера домена. В приведенном примере читается идентификационный номер учетной записи User3 (см. рис. 2) – UserID, которому соответствует поле uid. Объектная модель провайдера LDAP будет рассмотрена позже. Необходимо отметить, что свойства имеют все типы объектов – OU, CN и другие. Ïðèìåð 2: Set obj=GetObject("LDAP://CN=User3, OU=Group1, ↵ OU=Users, o=domain.ru") For Each U_obj In obj wscript.echo "UserUID: " & U_obj.Get("uid") Next
92
В том случае если доступ анонимным пользователям к записям блокирован, то необходимо читать свойства объектов от имени учетной записи, которая имеет права на чтение свойств. Пусть пользователь User1 имеет право на чтение всех полей объектов. Пароль пользователя User1 – 1234567. Осуществим чтение идентификационного номера учетной записи User3 от имени User1. Чтение параметров от имени другого пользователя осуществляется с помощью функции OpenDSObject(): Ïðèìåð 3: Set PreObj= GetObject("LDAP:") Set obj= PreObj.OpenDSObject("LDAP://CN=User3, OU=Group1, ↵ OU=Users, o=domain.ru", "CN=User1","1234567",0) For Each U_obj In obj wscript.echo "UserUID: " & U_obj.Get("uid") Next
Изменение атрибутов учетной записи Модификация атрибутов учетной записи осуществляется с помощью функции Put() и метода SetInfo, служащего для сохранения внесенных изменений в Active Directory. В приведенном ниже примере атрибут учетной записи User3 – Canonical Name (CN) будет изменен с User3 на User4. Ïðèìåð 4: Set PreObj= GetObject("LDAP:") Set obj= PreObj.OpenDSObject("LDAP://CN=User3, OU=Group1, ↵ OU=Users, o=domain.ru", "CN=User1","1234567",0) Set U_obj=obj.GetObject("InetOrgPerson","CN=User3") U_obj.Put "CN","User4" U_obj.SetInfo MsgBox "Ïàðàìåòð CN èçìåíåí."
InetOrgPerson – тип учетной записи.
Создание и удаление учетной записи Для создания учетной записи в Active Directory необходимо задать несколько обязательных параметров, относящихся к учетной записи и ее родительскому контейнеру: ! создание учетной записи должно выполняться пользователем, имеющим административные привилегии; ! путь к родительскому контейнеру, в котором необходимо создать учетную запись; ! класс создаваемого объекта; ! соответствующие свойства создаваемого класса объекта записи. Ïðèìåð 5: Set PreObj= GetObject("LDAP:") Set obj= PreObj.OpenDSObject("LDAP:// OU=Group1, OU=Users, ↵ o=domain.ru", "CN=User1","1234567",0) Set U_obj=obj.Create("InetOrgPerson","CN=User3") ClassArray=Array("InetOrgPerson","person","top","organizationPerson") U_obj.Put "objectClass", ClassArray U_obj.Put "cn", "User_Name_3" U_obj.Put "sn", "Second_Name_3" U_obj.SetInfo MsgBox "Ó÷åòíàÿ çàïèñü ñîçäàíà."
образование Для удаления учетных записей используется метод Delete («InetOrgPerson», object_name).
Заключение На практике управление Active Directory преимущественно осуществляется с помощью провайдера WinNT или в совокупности WinNT с LDAP. В «чистом» виде программирование LDAP используется очень редко. Основная причина заключается в том, что для доступа к любому объекту с помощью провайдера LDAP необходимо знать полный путь к этому объекту. Эта проблема легко решается: происходит осуществление поиска объекта, затем чтение его свойств. Приведем пример чтения поля FullName для пользователя USER1 с помощью провайдеров LDAP и WinNT. Ïðèìåð 6 a) - WinNT: Set obj=GetObject("WinNT:") For Each str In obj DomainName=str.Name Next Set UserName="Value" Set element=GetObject("WinNT://" & DomainName & "/ USER1") Msgbox "FullName: "+ cstr(element.FullName) Ïðèìåð 6 á) - LDAP: set rootDSE_ = GetObject("LDAP://RootDSE") DomainName = rootDSE_.Get("defaultNamingContext") UserLogonName="USER1" Const ADS_SCOPE_SUBTREE = 2 Set objConnection = CreateObject("ADODB.Connection") Set objCommand = CreateObject("ADODB.Command") objConnection.Provider = "ADsDSOObject" objConnection.Open "Active Directory Provider" Set objCommand.ActiveConnection = objConnection objCommand.CommandText = "SELECT name, sAMAccoutName ↵ FROM " _ & " 'LDAP://"& DomainName & "' ↵ WHERE objectClass='users'" objCommand.Properties("Searchscope") = ADS_SCOPE_SUBTREE objCommand.Properties("Cache Results") = False Set objRecordSet = objCommand.Execute
№3(16), март 2004
objRecordSet.MoveFirst Do Until objRecordSet.EOF If objRecordSet.Fields("sAMAccoutName").Value=UserLogonName msgbox "FullName: "+ objRecordSet.Fields("name").Value end if Loop
Как видно, листинг примера 6 б) в несколько раз больше, чем листинг примера 6 а). За счет того, что в примере 6 б) «просматривается» весь массив пользователей, сценарий будет отрабатываться в несколько раз медленнее, чем сценарий 6 а). Скорость выполнения сценария напрямую зависит от количества объектов в просматриваемом массиве. Очевидно, что чем больше размер массива, тем медленнее будет работать скрипт. Многократное использование данного механизма в одном скрипте дополнительно уменьшит скорость выполнения скрипта и увеличивает его размер по сравнению с аналогичным скриптом, в котором доступ к объектам осуществляется с помощью провайдера WinNT. Однако отказаться от доступа к AD с помощью провайдера LDAP невозможно, поскольку существует много функций, которые реализованы только в провайдере LDAP. Оптимальным вариантом является совместное использование провайдеров LDAP и WinNT. Ярким примером является чтение свойств сетевого принтера: с точки зрения провайдера WinNT принтер – локальное устройство, с точки зрения LDAP – сетевое.
Литература: 1. Коробко И. Active Directory – теория построения. – // Журнал «Системный администратор», №1(14), январь 2004 г. – 90-94с. 2. Коробко И. Программное управление ADSI: WinNT. – // Журнал «Системный администратор», №2(15), февраль 2004 г. – 66-74с. 3. Коробко И. Управление сетевыми принтерами домена. – // Журнал «Системный администратор», №10(11), октябрь 2003 г. – 38-46с.
93
подписка
Открыта подписка на II полугодие 2004 г. Более подробная информация на сайте www.samag.ru в разделе «Подписка»
Подписной индекс:
81655 по каталогу агентства «Роспечать»
Рады видеть Вас нашими читателями!
№3(16), март 2004
95
СИСТЕМНЫЙ АДМИНИСТРАТОР №3(16), Март, 2004 год РЕДАКЦИЯ Исполнительный директор Владимир Положевец Ответственный секретарь Наталья Хвостова sekretar@samag.ru Технический редактор Владимир Лукин Редактор Андрей Бешков Научно-технические консультанты Дмитрий Горяинов Валерий Цуканов РЕКЛАМНАЯ СЛУЖБА тел./факс: (095) 928-8253 Константин Меделян reсlama@samag.ru Верстка и оформление imposer@samag.ru maker_up@samag.ru Дизайн обложки Николай Петрочук 103045, г. Москва, Ананьевский переулок, дом 4/2 стр. 1 тел./факс: (095) 928-8253 Е-mail: info@samag.ru Internet: www.samag.ru РУКОВОДИТЕЛЬ ПРОЕКТА Петр Положевец УЧРЕДИТЕЛИ Владимир Положевец Александр Михалев ИЗДАТЕЛЬ ЗАО «Издательский дом «Учительская газета» Отпечатано типографией ГП «Московская Типография №13» Тираж 6600 экз. Журнал зарегистрирован в Министерстве РФ по делам печати, телерадиовещания и средств массовых коммуникаций (свидетельство ПИ № 77-12542 от 24 апреля 2002г.) За содержание статьи ответственность несет автор. За содержание рекламного обьявления ответственность несет рекламодатель. Все права на опубликованные материалы защищены. Редакция оставляет за собой право изменять содержание следующих номеров.
96
ЧИТАЙТЕ В СЛЕДУЮЩЕМ НОМЕРЕ: Transparent proxy. Быть или не быть?
Работа по расписанию в FreeBSD
Данная статья посвящена проблемам прозрачного проксирования на примере популярного сервера Squid. В качестве ОС использовалась стабильная версия FreeBSD 4.7. При работе проксисервера в прозрачном режиме (Transparent mode) для веб-доступа пользователей в Интернет не требуется настраивать браузер для взаимодействия с прокси на каждом рабочем месте, а сами пользователи могут вообще не знать о существовании прокси-сервера. В таком режиме администраторы и техники получают меньше вопросов и жалоб от пользователей по настройке пользовательского ПО. Технически этот режим реализуется следующим образом. С помощью брандмауэра все соединения на определённый порт (в случае HTTP – порт 80) внешних серверов перенаправляются на локальный порт прокси сервера (обычно – 3128). По стандарту протокола HTTP 1.1 (RFC2616) каждый запрос клиента должен содержать заголовок «Host», в котором указывается адрес сервераполучателя запроса. Именно с помощью этого заголовка прокси-сервер определяет адресата и соединяется с ним. Что же касается других популярных протоколов (FTP, HTTPS, и т. д.), то такой возможности в них просто не предусмотрено. На этой «весёлой ноте» можно начать описание проблем.
Способность операционной системы выполнять задачи в заданное время (по расписанию) может сделать администрирование существенно проще и эффективнее, а в ряде случаев такая способность просто необходима. В данной статье рассмотрены средства отложенной работы и работы по расписанию, имеющиеся во FreeBSD (на примере FreeBSD 5.1), однако большинство описанных здесь функций будут доступны вам и в других ОС семейства UNIX, включая Linux. Особое внимание уделено механизмам работы, знание которых необходимо для эффективного поиска возможных проблем.
Автоматизация веб-проектов через электронную почту Кто и как управляет сайтами? Информации об этом крайне мало, хотя, думаю, существует достаточное количество интересных решений. Наша задача – сделать как можно более удобный и доступный интерфейс для наполнения сайта контентом, то есть переложить кучу рутины на скрипты и сотрудников. Значит, нам предстоит заняться автоматизацией этого процесса.
NTP – атомные часы на каждом столе Каждый из нас, работая за компьютером, иногда смотрит на часы. При этом кто-то поглядывает на стену, ктото на руку, а кто-то бросает беглый взгляд в угол экрана монитора. Конечно, часы могут быть разными, главное – чтобы они шли точно. И если для обычных часов их точность во многом определятся фирмой-производителем, то для практически любых «компьютерных» высокая точность может быть достигнута благодаря синхронизации времени, например, с помощью протокола NTP (Network Time Protocol), об использовании которого и пойдет речь в данной статье.
Создание кластера на Windows 2000/2003. Шаг за шагом В этой статье я попытаюсь собрать свой опыт по созданию кластерных систем на базе Windows и дать небольшое пошаговое руководство по созданию двухузлового кластера серверов с разделяемым хранилищем данных.