Original version of this document is located here: http://www.haking.pl/pl/attachments/integrity_01_www.html

Article was first printed in Hakin9 magazine - http://www.hakin9.org


© Cybervlad


Проверка целостности установленной linux-системы до использования

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

Введение

Каждый владелец информации предпринимает те или иные меры по ее защите. Как минимум, при входе в систему необходимо предъявить имя и пароль пользователя, а в некоторых случаях информация хранится в зашифрованном виде, и для доступа к ней необходимо иметь криптографический ключ. Этих мер было бы вполне достаточно, если бы программное обеспечение, выполняющее функции проверки пароля и шифрования, было абсолютно надежным. Однако в жизни это совсем не так – в программах постоянно находят ошибки, которые могут быть использованы злоумышленником для получения контроля над системой или повышения своих привилегий. Для использования некоторых из этих ошибок злоумышленнику необходим физический доступ к компьютеру и минимальные права; чтобы воспользоваться другими ошибками, иногда достаточно послать через Internet несколько сетевых пакетов компьютеру-жертве. Если же злоумышленнику удалось получить контроль над компьютером, упомянутые выше программы уже не обеспечат защиту: пароль администратора и других пользователей может быть перехвачен установленной злоумышленником программой перехвата паролей (keylogger), а криптографические ключи могут быть легко прочитаны из памяти системы, даже если они обычно хранятся на съемных носителях (дискета, flash-drive, zip-drive, e-token и т.п.) и вводятся в компьютер только непосредственно перед расшифрованием  информации.

Для противостояния таким угрозам применяют различные методы: это своевременная установка обновлений (patches), правильная настройка программного обеспечения, удаление с компьютера неиспользуемого программного обеспечения, ограничение доступа к компьютеру через сеть при помощи межсетевых экранов (firewalls) или активного сетевого оборудования (фильтрующие маршрутизаторы – filtering routers). Еще одним эффективным методом защиты является использование систем обнаружения вторжений (Intrusion Detection System, IDS). IDS можно разделить на две большие категории: локальные (host based) и сетевые (network). Второй тип IDS (network IDS) обычно устанавливается на выделенном компьютере или маршрутизаторе и просматривает сетевые пакеты на предмет наличия в них злоумышленного трафика. Один из вариантов установки и настройки такой IDS описан в статье “Complete Snort-based IDS Architecture”, которую можно найти по адресу: http://online.securityfocus.com/infocus/1640/.

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

Средства проверки целостности

Целостность файлов можно проверять по-разному. Самое простое – сравнивать с эталоном имя файла, его размер, дату и время создания, а также права доступа к файлу и его атрибуты. Очевидно, что такой подход не обеспечивает достаточного уровня безопасности – при внесении изменений в файл не всегда меняется его размер, а метки времени (creation time, modification time and access time) можно легко вернуть в исходное состояние.

Другой способ заключается в расчете криптографических контрольных сумм (cryptographic checksum) файлов и сравнении их с эталоном. В качестве контрольных сумм обычно используют значения хеш-функций (hash-function), вычисленных по содержимому файла. Размерность значения хеш-функции зависит от используемого алгоритма (MD5, SHA-1 и т.п.). Этот подход дает большие гарантии обнаружения нарушения целостности, поскольку одним из свойств криптографической хеш-функции является стойкость по отношению к коллизиям. Это означает, что для любого набора данных можно легко вычислить значение хеш-функции, однако составить набор данных, значение хеш-функции для которого совпадало бы с заданным, является сложной вычислительной задачей.


Хеш-функцией называется функция, преобразующее набор данных произвольной длины в число фиксированной длины. Из этого следует, что случайные коллизии существуют в любой хеш-функции, поскольку область определения функции шире области значений функции. Но в данном случае речь идет именно о построении набора данных под заданное значение хеш-функции, что в большинстве случаев решается только при помощи полного перебора (bruteforce).


Недостатком рассмотренных подходов является необходимость хранения в безопасном месте эталонных значений. Действительно, если хранить эталонное значение хеш-функции вместе с контролируемым файлом, злоумышленнику не составит труда после изменения содержимого файла пересчитать и изменить хранимое значение хеш-функции. Поэтому набор эталонных значений, так называемую “known base”, хранят обычно на отчуждаемом носителе, например, на дискете.

От этого недостатка можно избавиться, если использовать цифровую подпись (digital signature) файлов. Для создания цифровой подписи используется секретный ключ (private key), а для проверки – открытый ключ (public key). Эти ключи однозначно связаны между собой, однако вычислить секретный ключ по открытому не представляется возможным. Точнее, если говорить строго, то при современном уровне развития компьютерной техники и математики, не существует способов сделать такое вычисление за приемлемое время. Минусом использования цифровой подписи является тот факт, что алгоритмы вычисления и проверки цифровой подписи являются более сложными, чем расчет контрольных сумм, а потому требуют больше процессорного времени на свое исполнение.

Для контроля целостности создано много программ, в том числе свободных, распространяемых по лицензии GNU GPL или ей подобных. В первую очередь это tripwire (http://www.tripwire.com), которая существует как в коммерческом, так и в свободном варианте. В состав дистрибутива Debian GNU Linux входят еще две программы со схожими функциями: integrit и bsign. Каждая из них имеет свои особенности, но все они замечательно справляются со своей основной задачей – обнаружением несанкционированных изменений в контролируемых файлах.

Однако, мы забыли еще об одном важном обстоятельстве. Если злоумышленник получил контроль над операционной системой, он может модифицировать ее таким образом, что она не позволит системе контроля целостности выполнять свои функции. Чтобы противостоять этой угрозе мы должны осуществить контроль целостности операционной системы до ее загрузки. Для этого обычно используют специализированные аппаратные решения, поставляемые в виде дополнительной платы и расширения BIOS extension. Но такие решения имеют достаточно высокую стоимость, требуют демонтажа аппаратной части для переноса на другой компьютер и не все из них умеют работать с файловой системой ОС Linuxext2/ext3/ReiserFS и т.п.

В качестве альтернативы аппаратным решениям можно использовать загрузку ОС Linux в две стадии. На первой стадии с проверенного носителя (дискета, CD-ROM, USB-drive) производится загрузка ядра операционной системы и контроль целостности системных и прикладных файлов на жестком диске. На второй стадии загрузка системы продолжается с жесткого диска.

Краткое описание процесса загрузки Linux

Загрузка ОС Linux может производиться в одну или две стадии.

При загрузке в одну стадию при помощи загрузчика (LILO, Loadlin, GRUB и т.п.) в память считывается образ ядра, которому при необходимости передаются дополнительные параметры. Получив управление, ядро монтирует корневую файловую систему (root filesystem) и запускает на выполнение файл /etc/init, который и осуществляет дальнейшую загрузку и настройку ОС.

При загрузке в две стадии загрузчик кроме ядра считывает в память образ электронного диска (RAM-disk), содержащего минимальную корневую систему. Следует заметить, что и ядро, и образ электронного диска являются упакованными при помощи gzip файлами, и за их распаковку также отвечает загрузчик. После получения управления ядро монтирует электронный диск как корень (root) и запускает на выполнение находящийся на нем файл /linuxrc. После завершения работы /linuxrc производится смена корневой файловой системы на реальную и управление передается файлу /etc/init, как и в случае загрузки в одну стадию. Содержимое RAM-disk будет доступно в каталоге /initrd или на устройстве /dev/initrd.

Загрузка в две стадии обычно используется в случаях, когда необходимо предварительно подготовить корневую файловую систему (например, если она зашифрована, проинициализировать ее, подключив к /dev/loop и запросив пароль для расшифрования). Все эти действия производятся программой /linuxrc, которая может быть любым исполняемым (executable) файлом: elf-binary или сценарием (script). Во втором случае на RAM-disk должен присутствовать соответствующий интерпретатор (sh, perl и т.п.).

Мы будем использовать загрузку в две стадии для решения нашей задачи по контролю целостности.

Выбор программы контроля

Какую программу контроля целостности использовать? Конечно, можно выбрать любую из перечисленных выше в соответствии с собственными предпочтениями. Однако следует помнить, что объем дискеты всего 1.44 Mb, а нам необходимо поместить на нее загрузчик, ядро и RAM-disk. Размер ядра может быть достаточно большим даже в сжатом виде – 700 kb и более. Поэтому программа контроля, которую мы поместим на RAM-disk, должна быть достаточно компактной. К сожалению, большинство известных программ контроля целостности, скомпилированных обычным способом, имеют достаточно большой размер. Например, исполняемый файл программы integrit имеет размер около 400 kb. Кроме того, для нормальной работы ей необходимы разделяемые динамические библиотеки (shared libraries), в частности libc и ld, суммарный размер которых составляет более 1 Mb. Если попытаться все это положить на RAM-disk, то получившийся в результате образ RAM-disk просто не поместится на дискету. Эту проблему можно решить двумя способами: либо использовать для загрузки носитель большой емкости (Zip-drive, IOMEGA-Jazz, USB-drive, LS-drive и т.п.), либо попытаться уменьшить размер программы контроля.

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

По этой причине в своей системе я решил использовать программу собственной разработки (sum28147) и скомпилировать ее при помощи пакета diet libc. Пакет diet libc предназначен для создания компактных и автономных приложений, не нуждающихся в наличии дополнительных разделяемых библиотек.


Пакет diet libc включен в состав большинства дистрибутивов. Например, для установки его в Debian GNU Linux достаточно дать команду:

# apt-get install dietlibc-dev

Если в вашем дистрибутиве он отсутствует, вы можете взять его в Internet. Сервер проекта расположен по адресу http://www.fefe.de/dietlibc/

Распакуйте полученный архив и дайте команду:

# make

В результате вы получите статическую библиотеку dietlibc.a и программу diet, которая будет помещена в один из каталогов, в зависимости от архитектуры процессора: bin-i386,
bin-ppc, bin-alpha, bin-sparc, bin-ppc
или bin-arm. Далее программу diet необходимо скопировать в какой-либо из каталогов, перечисленных в переменной окружения $PATH, например:
# install bin-i386/diet /usr/local/bin


Программа sum28147 использует для расчета контрольной суммы российский криптографический алгоритм ГОСТ 28147-89 в режиме выработки имитовставки с постоянным ключом. Поскольку в ней используются только конструкции ANSI C, ее можно легко скомпилировать на любой платформе.
В данном случае для компиляции программы при помощи diet libc я использовал следующий Makefile (См. Листинг 1).


Листинг 1. Makefile для сборки sum28147

sum28147: sum28147.c

 diet gcc -s -pipe -Os -DUNIX -o sum28147 sum28147.c

clean:

 rm -f sum28147

Исходный текст самой программы достаточно велик, чтобы приводить его в статье. Вы можете получить его вместе с Makefile в Internet по адресу http://www.free-unices.org/~cybervlad/checksum/checksum.tgz.

Программа очень проста в использовании и при запуске без параметров выдает краткую справку:

Usage: sum28147 {filename | dirname} | {opt parameter}

where opt/parameter is:

-l (or -L) listfilename - calculate checksum for files listed in listfilename

-V verifylist - verify files by verifylist

-v verifylist - same as above, but quiet (display only errors)

-o (or -O) filename - redirect output to filename

Если программе передать имя файла, она произведет расчет контрольной суммы и выдаст на стандартный поток stdout следующий набор данных, разделенных знаком “табуляция”:

<имя файла> <контрольная сумма> <размер> <дата модификации> <время модификации (UTC)>

Например:

# sum28147 /bin/bash
# /bin/bash 6BA9D15D8713D52E 511964 2002-04-08 19:07

При желании можно скомпилировать компактную версию и любой другой несложной программы подсчета контрольных сумм, например md5sum.

Далее следует определить перечень файлов, подлежащих контролю до загрузки. Хорошими кандидатами в этот список являются модули ядра системы (/lib/modules), используемые во время загрузки системы программы из каталогов /bin, /usr/bin, /sbin, /usr/sbin и т.п., а также стартовые сценарии (start-up scripts) из каталога /etc/init.d или аналогичных ему (это зависит от используемого дистрибутива Linux – организация стартовых сценариев в Debian, RedHat, Slackware и т.п. имеет свои отличия).
После определения перечня объектов необходимо рассчитать их контрольные суммы. Поскольку при проверке целостности корневая файловая система будет подмонтирована к каталогу /mnt на RAM-disk, перед созданием файла с контрольными суммами в работающей системе удобнее создать в каталоге /mnt соответствующие символические ссылки (symlinks). Например:

# ln -s /bin /mnt/bin
# ln -s /usr /mnt/usr

Теперь можно непосредственно приступить к созданию базы контрольных сумм. Для этого воспользуемся опцией -o, при помощи которой выводимые программой сообщения будут перенаправлены в файл:

# sum28147 /mnt/bin -o bin.lst

# sum28147 /mnt/usr/bin -o ubin.lst

Аналогичным образом создаем списки контрольных сумм и для других объектов. При выборе объекта необходимо учитывать, что он может оказаться символической ссылкой на другой объект с абсолютным, а не относительным путем, что может вызвать ошибки при проверке. Например, если файл /bin/sh является символической ссылкой на /etc/alternatives/sh, а не ../etc/alternatives/sh, то когда корневая файловая система будет подмонтирована в /mnt, ссылка будет указывать на несуществующий объект. В данном примере на RAM-disk нет файла /etc/alternatives/sh, искомый объект будет находится в /mnt/etc/alternatives/sh
Теперь, когда у нас есть эталонная информация для проверки целостности, мы можем приступить к созданию загрузочного диска.

Делаем загрузочный диск

Загрузочный диск можно изготовить путем копирования файла ядра и образа RAM-disk на дискету и последующей настройки загрузки при помощи команды rdev. Этот способ неудобен тем, что для внесения изменений придется переписывать весь диск, а также тем, что на диске нет файловой системы и его содержимое невозможно прочитать стандартными методами. Другой способ заключается в использовании дискеты в формате ms-dos (FAT16) и применении загрузчика ldlinux.sys для загрузки ядра.

Мы воспользуемся вторым способом.

Используем шаблон

В качестве шаблона при создании загрузочного диска можно использовать образ “аварийной” (rescue) дискеты из вашего дистрибутива. Я использую дистрибутив Debian GNU Linux. В этом дистрибутиве образ аварийной дискеты находится на первом (загрузочном) CDROM в каталоге dists/woody/main/disks-i386/current/images-1.44/ и называется rescue.bin.
Чтобы создать загрузочный диск, необходимо подготовить чистую отформатированную дискету и произвести на нее запись образа командой cat:

# cat rescue.bin > /dev/fd0

или командой dd:

# dd if=rescue.bin of= /dev/fd0

Если процесс записи прошел нормально, и не было сообщений об ошибках, можно подмонтировать вновь созданную дискету при помощи команды mount:

# mount /dev/fd0 /floppy

Каталог /floppy обычно уже создан, однако если это не так, вы можете создать его при помощи команды mkdir или использовать в качестве точки монтирования другой каталог.

Теперь можно посмотреть содержимое получившегося диска:

# ls /floppy

config.gz

debian.txt

f1.txt

f10.txt

f2.txt

f3.txt

f4.txt

f5.txt

f6.txt

f7.txt

f8.txt

f9.txt

install.sh

ldlinux.sys

linux.bin

rdev.sh

readme.txt

sys_map.gz

syslinux.cfg

type.txt

Большинство этих файлов нам не понадобится, и в дальнейшем мы их удалим или заменим.

Анализируем и перекомпилируем ядро

Ядро Linux позволяет компилировать поддержку определенных устройств или функций либо статически (соответствующие драйверы помещаются в основной файл ядра), либо динамически – в этом случае соответствующий код помещается в отдельные загружаемые модули. Как уже говорилось выше, мы ограничены доступным объемом дискового пространства на дискете, нам необходимо сделать ядро максимально компактным. С этой целью мы должны проанализировать, поддержка какого оборудования и функций нам нужна на первой стадии загрузки, а что можно скомпилировать в виде модулей. Например, для своей системы я принял решение скомпилировать в виде модулей поддержку звуковой платы Sound Blaster AWE64, поддержку ZIP-drive (ATAPI floppy), поддержку устройств SCSI и USB (для работы с flashcard-reader), поддержку NFS (Network File System), поддержку NLS (National Language Support), поддержку PPP и других функций, не нужных на первой стадии.

Обязательным условием для возможности осуществления загрузки в две стадии является поддержка RAM-диска и поддержка инициализационного RAM-диска. Для этого при настройке ядра необходимо включить следующие опции:

·         RAM disk support (CONFIG_BLK_DEV_RAM)

·         Initial RAM disk (initrd) support (CONFIG_BLK_DEV_INITRD)

Обратите внимание, что данные опции необходимо включить статически, а не в виде модулей, поскольку модули будут располагаться на реальной корневой файловой системе и на первой стадии загрузки будут недоступны.

Если используемое в вашей системе ядро имеет слишком большой размер, и вы решили его перекомпилировать, вам понадобятся исходные тексты ядра. Если они отсутствуют в вашей системе, вам придется установить соответствующий пакет (обратитесь к документации, поставляемой с вашим дистрибутивом) или загрузить архив с исходными текстами из Internet, с сайта www.kernel.org или его многочисленных зеркал.

Обычно исходные тексты ядра располагаются в каталоге /usr/src/linux. Если данный каталог отсутствует, и вы загружаете исходные тексты из Internet, вам придется создать его самостоятельно.


Может случиться так, что в системе установлено несколько версий исходных текстов ядра или вы просто хотите использовать более новую версию. Для сохранения предыдущих версий рекомендуется создать в каталоге /usr/src/ отдельные подкаталоги для каждой версии, а на текущую версию сделать символическую ссылку. Например:

# ls /usr/src
kernel-source-2.4.3-i
kernel-source-2.4.18
# ln -s kernel-source-2.4.18 linux

Итак, исходные тексты ядра получены и установлены, можно приступать к компиляции.

Для этого необходимо перейти в каталог с исходными текстами:

# cd /usr/src/linux

и произвести конфигурирование ядра в соответствии с приведенными выше рекомендациями. Для этого выполните команду:

# make menuconfig

или

# make config
Теперь можно непосредственно запустить процесс компиляции. Для этого выполните команды:

# make dep; make clean; make bzImage

Процесс компиляции ядра может занимать достаточно длительное время, это зависит от производительности вашего компьютера и объема установленной оперативной памяти (RAM). Получившийся в результате компиляции файл ядра помещается в каталог /usr/src/linux/arch/i386/boot под именем bzImage.

Далее, нам необходимо скомпилировать загружаемые модули ядра:

# make modules

Процесс компиляции модулей также может оказаться достаточно долгим. После компиляции модули необходимо установить. Команда:

# make modules_install

выполнит все необходимые действия и поместит модули в соответствующий подкаталог в каталоге /lib/modules.

Для обеспечения идентичности загрузки непосредственно с жесткого диска и с дискеты, а также для проверки работоспособности ядра, нам необходимо установить его и сообщить загрузчику о его местонахождении. Для этого файл ядра копируется в выделенный для этой цели каталог (обычно, это /boot, но в разных системах размещение файла ядра различно, иногда он размещается непосредственно в корне файловой системы) с уникальным именем (не удаляйте старые файлы ядра, они могут еще пригодиться!). Затем необходимо внести соответствующие изменения в файл /etc/lilo.conf (см. пояснения в рамке) и обновить загрузочную информацию командой lilo. Если в вашей системе используется не LILO, а другой загрузчик, например GRUB, обратитесь к поставляемой с ним документации.


Если вы желаете иметь возможность выбирать, какое ядро вы хотите загрузить, в файле /etc/lilo.conf необходимо раскомментировать параметр prompt. Для внесения нового ядра в список доступных для загрузки необходимо добавить в /etc/lilo.conf строчки вида:

image=/boot/kernel-chk
 label=kernel-chk
 read-only

В данном примере мы исходим из того, что скомпилированное ядро (/usr/src/linux/arch/i386/boot/bzImage) мы разместили в каталоге /boot под именем kernel-chk. Вы можете выбрать любое другое уникальное название для файла ядра и метки.

Более подробно о структуре файла /etc/lilo.conf и назначении параметров вы можете узнать из справочного руководства man 5 lilo.conf


Теперь мы можем проверить работоспособность ядра, для чего нам придется перезагрузить систему.

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

Подготовка RAM-disk

Чтобы не создавать RAM-disk полностью также воспользуемся в качестве шаблона готовым решением, в качестве которого можно использовать, например, мини-дистрибутив SPB-Linux версии 2.0. Дистрибутив состоит из двух дисков, архивы с которыми можно взять по адресу http://www.8ung.at/spblinux/index.htm
Из двух архивов disk1.zip и disk2.zip для нашей задачи нужен только первый – disk1.zip.
Загрузите и распакуйте этот архив, после чего найдите в подкаталоге boot файл ramdisk. Этот файл является упакованным образом RAM-disk. Для того, чтобы внести в него изменения, нам необходимо распаковать его и подмонтировать. Для этого переименуйте ramdisk в ramdisk.gz и воспользуйтесь командой gunzip:

# gunzip -d ramdisk.gz

Создайте временный каталог для монтирования:

# mkdir tmpram

и подмонтируйте файл как зацикленную (loop) файловую систему:

# mount ramdisk tmpram -o loop

Теперь в каталоге tmpram доступно содержимое нашего будущего RAM-disk и мы можем внести в него неообходимые изменения. Во-первых, необходимо скопировать исполняемый файл (executable) программы контроля целостности:

# cp sum28147 tmpram/bin/

Во-вторых, создать каталог для размещения файлов с контрольными суммами:

# mkdir tmpram/etc/sum28147

и скопировать в него созданные ранее файлы (в нашем примере - bin.lst и ubin.lst)


На используемом нами RAM-disk имеется программа busybox, которая выполняет функции большинства необходимых в работе утилит. Она располагается в каталоге var, и, в зависимости от того, под каким именем вызвана, выполняет соответствующие функции. Для этого на нее сделаны соответствующие символические ссылки. Однако, среди функций отсутствует одна очень нужная для использования в сценариях, а именно – ожидание ввода с клавиатуры. Для замены команды read проще всего написать небольшую программку и скомпилировать ее при помощи diet libc. Поскольку ее текст составляет всего несколько строчек, приведем ее и соответствующий Makefile полностью – см. Листинг 2 и Листинг 3.

Листинг 2. Программа read.c

#include <stdio.h>

void main(void){

getchar();

}

<<#LISTING>>


<<@LISTING|LANG=Make>>
Листинг 3. Makefile для сборки read.c

read: read.c

 diet gcc -s -pipe -Os -o read read.c


clean:

 rm -f read

Скомпилированную программу необходимо поместить в каталог var на RAM-disk.

Вы можете взять архив с приведенными файлами в Internet по адресу:

http://www.free-unices.org/~cybervlad/checksum/read.tgz


Теперь нам необходимо создать файл linuxrc, из которого непосредственно будет производиться запуск программы контроля.
Для этого замените находящийся на RAM-disk файл linuxrc на сценарий, приведенный в Листинге 4, внеся необходимые изменения.


Листинг 4. Сценарий linuxrc

#!/var/sh


/var/echo ========= start linuxrc ============

/var/mount /proc

/var/echo mount real root filesystem...

/var/mount /dev/hdc4 /mnt -t ext2

/bin/sum28147 -v /etc/sum28147/bin.lst

/var/echo Hit enter...

/var/read

/bin/sum28147 -v /etc/sum28147/ubin.lst

/var/echo Hit enter...

/var/read

/var/echo umount real root filesystem...

/var/umount /mnt

/var/umount /proc

/var/echo Hit enter...

/var/echo ======== finish linuxrc ============

/var/read

Данный сценарий выполняет следующие действия:

·         Монтирует специальную файловую систему proc (без этого невозможно будет использовать другие файловые системы). В данном случае на RAM-disk в файле /etc/fstab имеется строчка “proc /proc”. Если на используемом вами RAM-disk такого файла нет, вам необходимо его создать или производить монтирование командой “/var/mount proc /proc.

·         Монтирует реальную корневую файловую систему на каталог /mnt. Обратите внимание, что в сценарии необходимо заменить /dev/hdc4 на устройство, где размещается корневая файловая система в вашем случае.

·         Запускает программу проверки контрольных сумм для каталогов /mnt/bin и /mnt/usr/bin. Если вам необходимо контролировать другие объекты, внесите в сценарий соответствующие изменения. Файлы с эталонными значениями контрольных сумм можно объединить в один файл (поскольку они текстовые) и запускать sum28147 один раз.

·         Размонтирует контролируемую файловую систему и /proc. Размонтирование необходимо производить обязательно, поскольку если при завершении работы /linuxrc останутся смонтированными какие-либо файловые системы, дальнейшее поведения ядра не определено и может привести к сбоям и ошибкам.

На этом создание RAM-disk завершено и его можно размонтировать:

# umount tmpram

после чего упаковать:

# gzip ramdisk

и скопировать на дискету:

# cp ramdisk.gz /floppy

Также на дискету необходимо скопировать скомпилированное ранее ядро, назвав файл kernel:

# copy /usr/src/linux/arch/i386/boot/bzImage /floppy/kernel

Далее необходимо отредактировать находящийся на дискете файл syslinux.cfg:


Листинг 5. Файл syslinux.cfg

FONT font.psf
# see /usr/doc/syslinux/syslinux.doc.gz for file format description
DEFAULT kernel

APPEND vga=normal initrd=ramdisk.gz load_ramdisk=1 root=/dev/hdc4 init=/linuxrc

TIMEOUT 0

И снова, обратите внимание на необходимость указать вместо /dev/hdc4 устройство, где в вашем случае располагается корневая файловая система.
После удаления остальных файлов, которые нам не понадобятся, на дискете остается следующий набор:

# ls /floppy
config.gz

install.sh

ldlinux.sys

kernel

ramdisk.gz

rdev.sh

readme.txt

sys_map.gz

syslinux.cfg

type.txt

В моем случае размер файла kernel получился 715260 байт, а размер файла ramdisk.gz - 397374 байт. Эти размеры могут отличаться в зависимости от опций собранного ядра, используемой программы контроля целостности и ее вспомогательных файлов.
Наш загрузочный диск готов. Теперь его можно размонтировать и проверить в работе. Перед размонтированием не забудьте синхронизировать буфера дисковой подсистемы:

# sync; sync; umount /floppy

Практическая проверка

Перед загрузкой не забудьте проверить, какая последовательность загрузки (boot sequence) установлена в настройках BIOS (BIOS Setup) вашего компьютера.

В процессе загрузки с дискеты вы увидите сообщения о загрузке ядра и RAM-disk, процесс инициализации ядра, а затем работу сценария linuxrc. Если целостность не нарушена, сообщения будут выглядеть примерно так:

========= start linuxrc ============

mount real root filesystem...

Hit enter...

Hit enter...

umount real root filesystem...

Hit enter...

======== finish linuxrc ============

Если же целостность какого-либо из контролируемых объектов нарушена, вы получите сообщение об ошибке.

Чтобы промоделировать ошибочную ситуацию, я изменил время модификации (modification time) файла при помощи команды touch /bin/bash и получил следующий результат:
Error!

known: /mnt/bin/bash 6BA9D15D8713D52E 511964 2002-04-08 19:07

current: /mnt/bin/bash 6BA9D15D8713D52E 511964 2003-07-12 13:42

Получив подобное сообщение, необходимо проанализировать ситуацию: возможно, в вашей системе произошли несанкционированные изменения, а возможно вы сами что-то обновили, но забыли внести соответствующие изменения в файл с эталонными контрольными суммами (known base).

По окончании работы linuxrc произойдет дальнейшая загрузка системы с жесткого диска.

Заключение

Итак, мы получили простую возможность осуществлять проверку целостности установленной ОС Linux до ее использования. Такое решение является недорогим и эффективным, хотя и не абсолютно надежным, ведь при желании в компьютер можно установить аппаратную закладку (hardware spy-bug), которую достаточно сложно будет обнаружить даже при помощи специального оборудования, не говоря уже о программных методах контроля. Однако, такие способы добывания информации по силам только спецслужбам, и если вы опасаетесь, что против вас играет такой “противник”, то вы, вероятно, шпион или террорист :).

Если же вернуться к более прозаичным вещам, то существуют и недорогие аппаратные закладки, доступные простому человеку. В частности, в веб-магазине ThinkGeek любой желающий может приобрести устройство под названием KeyKatcher, которое “прозрачно” включается между клавиатурой и компьютером и может записывать до 65000 нажатий клавиш (см. http://www.thinkgeek.com/gadgets/security/5a05/). Но такое устройство легко обнаружить – достаточно осмотреть соединение клавиатуры с компьютером. Это еще раз подтверждает, что обеспечение безопасности – процесс комплексный, нельзя полагаться на одни лишь программные методы.

Созданный нами загрузочный диск вполне работоспособен, но не очень удобен в применении.

Во-первых, предлагаемый сценарий linuxrc является схематичным и нуждается в доработке под конкретные нужды. Например, можно уменьшить количество информационных сообщений и ожиданий ввода пользователя (hit enterread), объединить файлы с эталонными значениями контрольных сумм в один, а также анализировать код завершения (exit code) программы контроля и привлекать внимание пользователя только в случае возникновения ошибки.

Во-вторых, файлы с эталонными контрольными суммами располагаются на RAM-disk, и для внесения в них изменений необходимо файл с образом диска распаковывать, монтировать, а потом запаковывать обратно. Более удобным будет размещение этих файлов непосредственно на дискете, однако это потребует внесения изменений в сценарий linuxrc для ее предварительного монтирования и обязательной поддержки ядром файловой системы ms-dos (FAT16). Дополнительное преимущество такого способа хранения заключается в том, что можно предусмотреть интерактивное обновление или даже первоначальное создание этих файлов непосредственно из сценария linuxrc, а также сохранять протокол работы программы контроля целостности на дискету в виде файла. Конечно, это потребует существенной переделки сценария linuxrc и организации меню, но для опытных пользователей Linux это не составит большого труда. Недостатком этого метода является тот факт, что файлы с эталонными контрольными функциями будут храниться на дискете в несжатом виде и занимать больше места. Впрочем, этот недостаток также можно преодолеть – упаковать их, а перед использованием распаковывать на RAM-disk. Говоря о размерах файлов с эталонными контрольными суммами, нельзя не упомянуть о возможности контролировать целостность в два этапа. При помощи описанной в данной статье загрузочной дискеты контролируются только самые критичные элементы (модули ядра, системные библиотеки, программы из каталогов /sbin и /usr/sbin, а также другие, используемые в процессе загрузки) и файлы “больших” систем контроля целостности типа tripwire или integrit. Контроль остальных объектов можно осуществлять после окончательной загрузки операционной системы при помощи упомянутых традиционных систем.

Приведенный в данной статье материал также можно использовать и для создания загрузочного USB-устройства, если такая возможность имеется в BIOS вашего компьютера. Среди очевидных преимуществ USB-устройства перед дискетой можно в первую очередь назвать компактность, высокую надежность и повышенную емкость.
И последнее. Не забывайте, что загрузочный носитель с программой контроля целостности является критичным элементом безопасности вашей системы, храните его в защищенном месте, ведь если он будет лежать на столе рядом с компьютером, толку от него не будет никакого :).

По просьбам читателей :)
RAMDISK (сжатый), который я сделал в процессе написания статьи для своего домашнего компьютера, можно взять здесь: ramdisk
На нем уже есть статически слинкованная программа sum28147 и файл со списком контрольных сумм, который вам надо будет модифицировать для своей системы. Не забудьте также поправить linuxrc.
Образ загрузочной дискеты лежит здесь: rescue.bin
После записи образа на дискету ее надо модифицировать, как описано в статье (убрать лишнее, отредактировать syslinux.cfg, положить модифицированный ramdisk и скомпилированное под ваше "железо" ядро)