Несанкционированный пользователь может повредить команду login при помощи тех же методов, что и для passwd. Тем не менее, администратор может сделать нечто большее, чем просто защитить данную программу от повреждения. По теории, наилучшей защитой является нападение. Поэтому администратор может внести в команду login свои собственные правки и применять ее как систему оповещения о вмешательстве. Каждый раз, когда кто-то входит в систему или пытается войти, можно изменить запись об используемых имени и пароле. Это может сигнализировать вам о любых попытках нарушителей угадать пароли методом грубой силы.
В силу способа, которым реализована команда login, требуется только одно изменение. Алгоритм проверки как пользовательского пароля, так и пароля при наборе номера для модемной связи вызывает одну и ту же подпрограмму. К сожалению, мы не можем привести ее здесь, так как несанкционированные пользователи смогли бы применить ее для сбора паролей в своих корыстных целях. Далее, если вы завели ваш собственный секретный файл для протоколирования попыток входа в систему, то вы должны попытаться убедить нарушителей, что они не смогут прочитать его и останутся со своими заботами. Вы можете сделать следующее, хотя это увеличило бы накладные расходы: зашифровать утилитой cryt пароли в протокольном файле с применением вашего собственного секретного ключа. Тогда даже если кто-то прочитает этот файл, он не сможет воспользоваться этой информацией.
Рассматриваемые ниже случаи представляют собой менее, но все же потенциально болезненные проблемы безопасности. Они не привлекают способов овладения суперпользовательскими привилегиями, но являются способами надувательства системы и бегства из нее.
Данный прием, пожалуй, редко применяется, если только у вас нет человека, который очень близко знаком с низкоуровневым функционированием того или иного процессора, используемого в вашей машине. Он может проникнуть в сердце аппаратного оборудования и пристроиться поверх операционной системы. Тем не менее, администраторы должны осознавать, что такие вещи возможны.
Во многих процессорах, например в процессоре Motorola 68000, имеется регистр состояния процессора (Processor Status Register), называемый обычно PSW, хотя у разных процессоров он может называться по -разному. PSW содержит бит, определяющий, работает ли машина в "супервизорном" или в пользовательском режиме. Этот режим важен для многопользовательской аппаратуры, поскольку все пользовательские программы работают в пользовательском режиме, что сегментирует и защищает память от "коллизий" между процессами.
С другой стороны, ядро работает в супервизорном режиме. Это означает, что защита памяти не действует и центральный процессор может изменять содержимое любой ячейки памяти во всей машине. Ядру необходима такая возможность, поскольку ядро поддерживает механизм своппинга для перемещения процесса в защищенную память и из нее, когда процесс выполняется.
Если нарушители безопасности могут получить в прграмму, работающую в системном режиме, то они получают возможность изменять всю память в системе. Последствия могут варьироваться от абсолютного разрушения, например записывания нулей в каждую ячейку памяти, до способности читать и сортировать данные в памяти, включая пароли и другую информацию с очень ограниченным к ней доступом.
Для того чтобы добиться системного режима, нарушителю необходима возможность сгенерировать и установить новое ядро. Используемый метод зависит от того, есть ли у нарушителя исходный текст программ ядра. Приводимые нами подробности относятся к процессору 68000, но могут быть аналогичными для других процессоров.
Первый метод - создать "пользовательский" системный вызов. Системные вызовы находятся в исходных файлах с именами вида os/sys?.c. Это примерно 60 системных вызовов, и каждый из них имеет специфический номер. Этот номер определяется таблицей системных входов - таблицей адресов точек входа в системные вызовы. Для добавления нового системного вызова необходимо подготовить его исходный код. Когда ядро перекомпилировано и установлено, можно производить системный вызов из любой программы в системе. Как только такой вызов активизирован, он может перевести машину в системный режим.
К счастью, не так уж легко для "обычного" пользователя перекомпилировать и переустановить ядро системы. Этот метод, вероятно, требует "внутренней работы". Помогло бы хранение ваших исходных текстов подальше от системы, но если вам нужно иметь системных програмистов, регулярно модифицирующих эти исходные тексты, то все, что в ваших силах - ограничить доступ (и подобрать надежных людей)!
Второй метод может быть использован теми нарушителями, которые не имеют исходных текстов, но имеют все библиотеки, образующие ядро. Здесь подход несколько другой, но результат тот же. В соответствии с той же идеей системного режима, цель заключается в том, чтобы в регистре PSW центрального процессора был установлен привилегированный доступ ("супервизорный" или "системный режим"). Вместо того, чтобы использовать надлежащим образом ядро, этот метод пользуется внешним драйвером, который связывается с ядром. Это выполняется путем создания псевдоустройства. Псевдоустройство подобно настоящему устройству, но его имя не ведет ни к какой физической периферии. Доступ к псевдоустройству осуществляется с помощью всех тех же самых примитивов (открыть, закрыть, читать, писать), но это доступ к логической области, а не к физической.
Для того чтобы определить псевдоустройство, нужно модифицировать главный файл устройства. В главном файле (который называется /etc/master или /usr/sys/conf/master) имеется таблица всех имен драйверов устройств, связанных с каждым примитивом. Когда создается псевдоустройство, в таблицу драйверов устройств помещается новая запись. В этой таблице содержатся имена всех подпрограмм, поддерживающих примитивы.
Привилегированного режима можно добиться при помощи открытия псевдоустройства. Системный вызов open передает управление драйверу устройства, т.е. добавленному коду. В момент запуска этого кода машина уже находится в системном режиме, поскольку когда выполнялся вызов open, он был "пойман" системой и передан программе обработки, функционирующей в системном режиме. После этого драйвер устройства может делать то, что он хочет.
Если команда login подобна сторожу крепости, то программа uucp подобна заброшенному спасательному туннелю, через который враги могут проникнуть во дворец. С приходом межмашинных коммуникаций возникает целый ряд пробоин в защите системы.
При помощи uucp несанкционированные пользователи могут попасть в систему, выдав себя за удаленный узел uucp. Это очень легко сделать. Нарушители могут заглянуть в файл /usr/lib/uucp/L.sys в вашей системе и обнаружить, где находятся удаленные системы, путем поиска входов в систему на других машинах. Затем они могут посмотреть в файле /etc/passwd такие входы в систему, которые запускают программы uucico вместо обычного shell-интерпретатора. Если они обнаружат соответствующие пароли, они могут попытаться применить некоторые вероятные пароли или использовать один из методов внесения правок, рассмотренных ранее, с целью перехвата паролей.
Затем нарушитель может изменить имя узла своей системы на имя узла удаленной системы, чтобы выдать себя не за того, кем он на самом деле является. Он может войти в систему под именем uucp или под специальным регистрационным именем, предназначенным для удаленной машины. Программы uucp передают это узловое имя (которое является поддельным) в вашу систему.
Нарушители могут перекачать почту, файлы и т.д. из вашей системы на свою машину. Если у вас есть что-нибудь в очереди, ожидающей отправки на законную удаленную машину, нарушители могут сразу там очутиться. Вы можете столкнуться с неприятностями, когда один из ваших операторов законной удаленной системы звонит и спрашивает вас, почему он неделями не получает от вас ни почты, ни программных запросов! Однако, коварный нарушитель мог бы переслать копию украденных файлов обратно к вам и использовать прогрессивные средства для отправки их на законную удаленную машину.
Этот прием довольно хорошо известен, но мы включаем его для полноты изложения. Похоже, однако, что он работает не во всех версиях системы UNIX. Он работает в System V, но не работает в XENIX, System III и Berkeley 4.2. Данный метод заключается в изменении пользовательской переменной среды LOGNAME. Поскольку команда mail использует ее, чтобы идентифицировать вас при отправке вам почты, меняется заголовок почты. Обычно это всего лишь мелкая неприятность, но вы должны уведомить об этом пользователей, чтобы они очень внимательно относились к таким сообщениям, которые кажутся несвойственными для их мнимого отправителя.
Полезной практикой для обеспечения безопасности является выполнение случайных команд ps. Такая мера более-менее равносильна периодическому патрулированию с целью увидеть, не происходит ли что-нибудь опасное. Необходимо, однако, отметить, что лица, использующие редактор vi для несанкционированной работы, могут замести свои следы, выбирая такое имя редактируемого файла, чтобы оно не появлялось в распечатке команды ps. Самый простой способ, которым они могут это сделать - вызвать vi без указания имени файла. Тем самым vi запускается с пустым файлом. Затем они могут применить команду ex для редактирования нужного им файла. Это убережет имя файла от распечатки командой ps, так как оно не является частью набора аргументов команды vi. Массив аргументов формируется при вызове команды vi, а не после ее запуска.
Другой способ - использовать маскировку. Нарушители могут переименовать файл, который они хотят редактировать, в ничего не означающее имя, например tmp, а потом использовать имя tmp при вызове редактора vi. В результате в массив аргументов занесется имя tmp. Оно и появится в распечатке команды ps.
ИМЯ: access access
Ищет в парольном файле все регистрационные записи, не имеющие паролей.
access
access Выдает список всех беспарольных входов в систему
1 : 2 # @(#) access v1.0 Show all free access logins Author: Russ Sage 4 if [ "$#" -gt "0" ] 5 then echo "access: too many arguments" >&2 6 echo "usage: access" >&2 7 exit 1 8 fi 10 grep '^[^:]*::' /etc/passwd || echo "All logins protected"
Зачем нам нужен командный файл access?
Мы уже отмечали, что записи о входе в систему в парольном файле создают возможность нарушения защиты, если с ними не связаны пароли, т.е. если поле пароля пустое. Проблема заключается в том, что в больших системах парольный файл может сильно разрастись. Искать в таком файле вручную регистрационные записи, в которых отсутствуют пароли, было бы утомительным и приводило бы к ошибкам. Почему бы не поручить системе сделать за вас эту работу?
Что делает access?
Командный файл access использует команду grep с шаблоном поиска, описывающим регистрационную запись, не имеющую пароля. Когда такая запись попадается, она печатается в стандартный вывод. Если указанных записей не найдено, выводится сообщение "All logins protected" ("Все входы в систему защищены").
Первое, что делает access (в строках 4-8) - проверяет, правильно ли она была вызвана. Поскольку опций не предусмотрено, в командной строке ничего не должно быть. Если количество аргументов в командной строке больше нуля, то на стандартное устройство регистрации ошибок выдается сообщение об ошибке и командный файл завершается.
Оператор в строке 10 выполняет поиск в парольном файле. Применяется утилита grep, т.к. мы используем в этой команде выражение. Если бы мы использовали фиксированную строку, более предпочтительной была бы утилита fgrep, потому что она быстрее. Выражение, задающее поиск, означает следующее: начиная с начала строки, найти все символы, отличные от двоеточия, вплоть до обнаружения двух двоеточий подряд. Если вы заглянете в файл /etc/passwd, то увидите, что первое поле представляет собой имя (от начала строки до первого двоеточия). Затем между первым и вторым двоеточием размещается пароль. Если пароль отсутствует, то после первого двоеточия сразу же следует второе - именно это соответствует нашему шаблону поиска. Поиск выполнятся в файле /etc/passwd. Если grep успешно обнаружил хотя бы одну запись, то код возврата нулевой. Если grep ничего не обнаружил, то код возврата единица. Тогда активизируется последняя часть строки 10 и выводится сообщение о том, что все записи о входе в систему защищены.
ИМЯ: chkset chkset
Выдает список всех файлов, имеющих включенный бит разрешения установки пользовательского/группового идентификатора.
chkset [-l] [dir ...]
chkset -l
Вести поиск, начиная с корневого каталога, поскольку каталог не указан. С помощью команды "ls -d" выдать список файлов, для которых установлен в единицу бит разрешения установки идентификатора пользователя либо идентификатора группы. Результат отсортировать по имени файла. (Бит установки пользовательского идентификатора S_ISUID и бит установки группового идентификатора S_ISGID являются атрибутами защиты файла наряду с битами прав доступа на чтение/запись/выполнение и определены в подключаемом файле /sys/stat.h. - Примеч. перев.)
1 : 2 # @(#) chkset v1.0 Check for set bits on Author: Russ Sage 4 FORM="-print" 5 SORT="sort" 7 if [ "`echo $1 | cut -c1`" = "-" ] 8 then case $1 in 9 -l) shift 10 FORM="-exec ls -ld {} ;" 11 SORT="sort +7";; 12 *) echo "usage: chkset [-l][file/dir ...]" >&2 13 exit 1;; 14 esac 15 fi 17 if [ "$#" -gt 0 ] 18 then SRC="$*" 19 else SRC="/" 20 fi 22 find $SRC \( -perm -4000 -o -perm -2000 \) $FORM | $SORT
FORM | Команда и опции для листинга |
SORT | Команда и опции для сортировки результата |
SRC | Исходный каталог, от которого нужно начинать поиск |
Зачем нам нужен командный файл chkset?
Мы уже рассмотрели проблемы безопасности, которые могут возникнуть, когда для исполняемых файлов установлен в единицу бит разрешения установки идентификатора пользователя. Это означает, что они могут запускать интерпретатор shell с корневой или с другой привилегией высокого уровня. С той же целью может быть установлен в единицу бит разрешения установки идентификатора группы. Поэтому системный администратор должен непрерывно разыскивать и проверять все файлы в системе, для которых установлены эти биты, чтобы посмотреть, не используются ли они для несанкционированных целей.
Не для всех интерпретаторов shell, нарушающих защиту, владельцем является суперпользователь (root). Один пользователь может запустить shell, владельцем которого является другой пользователь, имеющий более высокие привилегии. Это фактически предоставляет пользователю, запустившему shell, все возможности владельца файла.
Найти shell-интерпретаторы, устанавливающие идентификатор пользователя или группы, бывает легко, а бывает и трудно, в зависимости от их авторства. Легко найти такие, которые:
а) имеют необычные имена (некоторые нарушители любят выставлять свои достижения напоказ);
б) содержат в исполняемом файле символьные строки, которые можно прочитать;
в) размещены в необычном или очевидном каталоге;
г) не имеют ограничений относительно того, кто может их запустить.
Для изощренных shell-интерпретаторов характерно следующее:
а) они имеют имена, похожие на обычные команды системы UNIX;
б) имеют размеры файлов, которые соответствуют другим файлам, размещенным неподалеку от них;
в) содержат упрятанные символьные строки, которые не так-то легко прочитать;
г) имеют, возможно, специальную опцию, запускающую shell, или даже специальный пароль, необходимый для его запуска.
Самые хулиганские из них являются настоящими командами системы UNIX, переделанными в корневые shell-интерпретаторы, которые затем переустанавливаются вместопервоначальных shell-интерпретаторов. Единственный надежный способ идентифицировать этот последний тип запустить побайтовое сравнение между вашей дистрибутивной копией команды системы UNIX и той версией, которая присутствует в настоящий момент в вашей системе.
Конечно, перед тем как вы сможете проверить файлы со включенными битами разрешения установки пользовательского/группового идентификатора, вам нужно найти все такие файлы. В системе UNIX это делают две команды: find(1) и ncheck(1M). Утилита find ищет файлы по нашей точной спецификации и может быть использована для поиска файлов с заданным набором прав доступа, включая биты установки идентификаторов. Ncheck печатает вперемешку специальные файлы и файлы с разрешенной установкой идентификатора пользователя. Это очень большой список, чтение и поиск в нем интересующих нас файлов занимает много времени. Таким образом, полезно создать командный файл, предоставляющий нам всю необходимую информацию и только такую информацию. Chkset использует команду find и ищет только файлы со включенными битами разрешения установки пользовательского/группового идентификатора, так что результат содержит лишь необходимую нам информацию, и этим результатом можно сразу же воспользоваться.
Что делает chkset?
Chkset имеет два режима функционирования: один для сканирования всей системы в целом, а другой для сканирования указанных деревьев каталогов. Это хорошее свойство, так как сканирование каждого файла системы занимает очень много времени. Если имеется много больших дисковых устройств, проверка всего содержимого системы может занять целый час. Утилита chkset также очень сильно загружает центральный процессор из-за всех процессов, которые она генерирует. Указывая имена каталогов, вы можете выполнить проверку лишь на определенной области системного дерева. Отметим, однако, что поскольку chkset пользуется командой find, она сканирует не только указанный вами каталог, но и ВСЕ подчиненные каталоги. Заметим также, что chkset обнаруживает ВСЕ файлы с установленными в единицу битами установки пользовательского/группового идентификатора, а не только те, владельцем которых является суперпользователь (root).
Результат работы chkset можно выдать также двумя способами. Если не применять никакую опцию, то форма выдачи результата определяется командой "find ... -print", что означает полные маршрутные имена найденных файлов. Затем эти полные имена сортируются. Если применяется опция -l, то для форматирования результата используется команда "ls -ld", порождающая длинный формат листинга. При этом распечатываются полное указание прав доступа, число связей, владелец, размер и имя файла. Этот результат также сортируется по именам. Выбирайте тот или иной формат в зависимости от обстоятельств. Если вам нужно проверить большой участок системы и получить список подозрительных файлов, примените листинг по умолчанию (без опций), так как он компактнее и, как мы увидим позднее, занимает значительно меньше процессорного времени. Если вы хотите заняться определенным каталогом и посмотреть на его файлы подробно, воспользуйтесь опцией l. Она предоставляет больше информации и избавляет от необходимости вручную набирать команды ls для интересующих вас файлов.
Отметим, что эту команду может запустить кто угодно, а не только администратор, имеющий привилегии суперпользователя. Однако, если она запускается обычным пользователем, то команда find внутри командного файла chkset ограничена теми файлами, к которым пользователь имеет доступ на чтение. Так что вы могли бы предложить более привилегированным пользователям запускать chkset в качестве одной из их личных мер безопасности. Разумеется, если chkset запускается суперпользователем, то ограничения прав доступа к файлам несущественны и можно подвергнуть проверке все файлы.
# chkset /bin /usr/bin /lib /usr/lib
Эта команда вызывает просмотр указанных каталогов. В каталогах типа /usr/lib просматриваются все подчиненные каталоги, что обеспечивает более тщательную проверку.
Первым делом chkset инициализирует две переменные - FORM и SORT. Переменная FORM содержит команду для выдачи результата работы команды find, а переменная SORT - команду, определяющую, что нужно сортировать.
В строке 7 проверяется, является ли первый позиционный параметр опцией. Если да, то оператор case (строки 8-14) смотрит, какая это опция. Если это опция "-l", то подготавливается команда для распечатки результата (это мы обсудим позже). Команда для утилиты sort формируется так, чтобы сортировка шла по полю владельца. Опция убирается из командной строки, потому что все последующие аргументы должны быть каталогами и мы захотим получить к ним доступ с помощью "$#". Если попалась опция, отличная от "-l", то это ошибка, выдается сообщение об ошибке (строка 12), и командный файл завершается.
Если осталось более нуля аргументов, когда мы попадаем в строку 17, то они проверяются в цикле, чтобы убедиться, что все они являются каталогами. Если это не каталоги, на стандартное устройство регистрации ошибок выдается сообщение об ошибке и командный файл завершается. Если имеются параметры (т.е. каталоги), то в строке 18 в переменную SRC заносятся все каталоги. Если же параметров нет, то в переменную SRC заносится значение "/", т.е. корневой каталог, чтобы обеспечить подразумеваемую стартовую точку для поиска.
Вся работа этого командного файла выполняется фактически в операторе find. Команда find допускает множественное указание каталогов, которые поступают в результате чтения их из командной строки и занесения в переменную SRC.
После того как мы указали команде find, откуда начинать поиск, мы указываем ей, что нужно искать. В данном случае нас интересуют все файлы, которые имеют включенный бит установки пользовательского либо группового идентификатора. Мы объясняем это команде find путем указания прав доступа, которые требуется искать. Строка "-perm -4000" означает поиск всех файлов, имеющих права доступа со включенным битом установки пользовательского идентификатора и с любыми другими включенными битами. Вы можете понимать эту запись как применение символов -заменителей - 4???. Мы ищем как установку пользовательского идентификатора (-4000), так и установку группового идентификатора (-2000), поэтому две строки прав доступа соединены опцией -o, означающей "or" ("или"). (Более полное описание прав доступа в символической и восьмеричной форме приведено в chmod(1).)
Следующая задача - добавить строку, хранимую в переменной FORM, в командную строку. Если опция -l не была использована, то в переменной FORM хранится строка "-print", а это значит, что find будет печатать маршрутные имена найденных файлов. Если же -l использовалась, то переменная FORM содержит строку "-exec ls -ld {} ;". Опция -exec это очень гибкая опция команды find, позволяющая применить любые команды, которые за ней следуют, к каждому найденному файлу. В данном случае эти команды выполняют распечатку в длинном формате (-l), причем для каждого каталога (-d) выводится только его имя (а не содержимое). Именно здесь происходят наибольшие затраты ресурсов центрального процессора, так как для опции -l требуется системный вызов stat.
Из-за того, что для каждого файла требуется команда ls, она каждый раз загружается в память и выполняется. Производится также доступ к индексному дескриптору файла на диске. Это приводит к большим накладным расходам.
Затем весь поток данных пропускается через утилиту sort. На самом деле мы хотим сделать сортировку по восьмому полю (вы можете проверить это, выполнив команду "ls -l" и изучив ее результат). Утилита sort ПРОПУСКАЕТ указанное число полей, начиная с поля 1, являющегося по умолчанию стартовой точкой, поэтому использование записи +7 означает переход к восьмому полю, которым является имя файла.
Для поиска файлов с интересующими нас правами доступа можно применить другой метод, хотя и более медленный. Он основывается на том, что вместо поиска прав доступа по числу, можно искать их по символьной строке. Для этого нужно применять grep. Альтернативная команда выглядит так:
# find $* -exec ls -ld {} \; | grep "^[^ ]*s[^ ]*"
Этот вариант команды работает несколько иначе. Он находит каждый файл из указанных каталогов и применяет к каждому из них команду "ls -ld". Затем весь список данных передается по конвейеру команде grep. Grep использует для распознавания интересующих нас файлов такой шаблон поиска: начиная с начала строки, найти повторяющийся символ, отличный от пробела, затем символ "s", за которым следует повторяющийся символ, отличный от пробела. Такому шаблону соответствуют все режимы прав доступа, содержащие s, будь то бит установки пользовательского идентификатора или бит установки группового идентификатора. Не существенно, входят ли в данный режим "r", "w", "x" или "-". Убедившись, что пробелов нет, мы обеспечиваем соответствие шаблона только полю прав доступа. Важно, чтобы после s следовали символы, отличные от пробелов, так как s может встретиться либо в порции прав доступа, относящейся к владельцу, либо в групповой порции и нигде больше. Мы не очень рекомендуем этот метод, поскольку он привлекает очень интенсивную обработку. Однако, если бы мы не испробовали этот метод первым, мы бы не оценили, что использование команды find со строками "-perm" является более предпочтительным. В силу невероятной гибкости системы UNIX, имеется очень много различных способов выполнения одной и той же работы, и многие из этих способов могут давать одинаково хорошие результаты, но за разную цену в смысле быстродействия и процессорных затрат. Перед тем как применять универсальную утилиту вроде grep для решения задачи, рассмотрите, нет ли другой команды со встроенной опцией распознавания по шаблону. Скорее всего, применение такой команды окажется оптимальнее и гораздо быстрее, чем использование grep. С другой стороны, иногда вам нужно быстро решить нечасто встречающуюся задачу, не слишком заботясь об эффективности решения.
ИМЯ: suw suw
Просматривает протокольный файл команды su и печатает имена всех пользователей, которые нелегально превратились в суперпользователя при помощи команды su (substituted user, замененный пользователь).
suw [-m] [sulog]
suw Запуск в режиме по умолчанию, проверка файла /usr/adm/sulog и выдача записей о нарушителях в стандартный вывод.
1 static char id[]="@(#)suw v1.0 Author: Russ Sage"; 3 # include 5 # define FALSE 0 6 # define TRUE 1 7 # define MATCH 0 8 # define BSIZ 80 10 main(argc,argv) 11 int argc; 12 char *argv[]; 13 { 14 register int alert, c, mail, n; 15 FILE *fp1, *fp2; 16 char *p, *uname, line[BSIZ], tmp[BSIZ], 17 *log = "/usr/adm/sulog"; 19 static char *legal[] = {sage-root\n","root-root\n",NULL}; 20 static char *adm[] = {"sage",NULL}; 23 mail = FALSE; 25 if (argc > 1 && argv[1][0] == '-') 26 switch (argv[1][1]) 27 { 28 case 'm': 29 mail = TRUE; 30 --argc; 31 ++argv; 32 break; 33 default: 34 fprintf(stderr,"suw: invalid argument %s\n", argv[1]); 35 fprintf(stderr,"usage: suw [-m] [sulog]\n"); 36 exit(1); 37 } 39 if (argc == 2) 40 log = *++argv; 42 if ((fp1 = fopen(log,"r")) == NULL) 43 { 44 fprintf(stderr,"suw: error opening %s\n",log); 45 fprintf(stderr,"usage: suw [-m] [sulog]\n"); 46 exit(1); 47 } 49 sprintf(tmp,"/tmp/suw%d",getpid()); 50 if ((fp2 = fopen(tmp,"w+")) == NULL) 51 { 52 fprintf(stderr,"suw: error opening %s\n",tmp); 53 fprintf(stderr,"usage: suw [-m] [sulog]\n"); 54 exit(1); 55 } 57 while (fgets(line,sizeof(line),fp1) != NULL) 58 { 59 p = line + 15; 60 if (*p == '+') 61 { 62 p = p + 2; 63 while (*p != ' ') p++; 64 p++; 65 uname = p; 66 while (*p && *p++ != '-') 67 continue; 69 if (strcmp (p,"root\n") == MATCH) 70 { 71 alert = TRUE; 72 for (n=0; legal[n] != NULL; n++) 73 if (strcmp (uname,legal[n]) == MATCH) 74 { 75 alert = FALSE; 76 break; 77 } 78 if (alert) 79 fprintf(fp2,"Illegal --> %s", line); 80 } 81 } 82 } 84 if (mail) 85 { 86 fclose(fp2); 87 for (n=0; adm[n] != NULL; n++) 88 { 89 sprintf(line,"cat %s | mail %s",tmp,adm[n]); 90 system(line); 91 } 92 } 93 else 94 { 95 rewind(fp2); 96 while ((c = getc(fp2)) != EOF) 97 putc(c, stdout); 98 fclose(fp2); 99 } 101 fclose(fp1); 102 unlink(tmp); 103 }
Зачем нам нужна программа suw?
Вы помните, что команда su, позволяющая пользователям изменять свою индивидуальность (и права доступа) может быть источником проблем безопасности. Система хранит протокол всех транзакций su в файле sulog. Хотя более опытные нарушители могут уметь затирать свои следы, файл sulog полезен для отслеживания потенциальных лазеек в системе защиты. Этим способом можно поймать многих нарушителей-любителей. Естественно, мы хотим автоматизировать этот процесс, чтобы система выполняла проверку и сигнализировала нам при обнаружении чего-либо опасного. Кроме того, данная программа демонстрирует методику, которую можно использовать для отслеживания других протокольных файлов.
Что делает suw?
Программа suw читает и анализирует протокольные файлы команды su. Каждое успешное превращение в суперпользователя при помощи команды su, обнаруженное в протокольном файле, сверяется со списком разрешенных суперпользователей. Если пользователю не разрешено быть суперпользователем, токонкретная запись о нем печатается, чтобы оповестить администратора.
По умолчанию записи о нарушителях печатаются в стандартный вывод. Протокольным файлом по умолчанию является /usr/adm/sulog. Если применяется опция -m, то записи о нарушителях рассылаются по почте администраторам, занесенным в предопределенный список. Если нужно проверить другой протокольный файл, например /usr/adm/Osulog, то его имя можно указать в командной строке.
1. # suw -m
Проверить файл /usr/adm/sulog и разослать записи о нарушителях администраторам, определенным в тексте программы.
2. # suw /usr/adm/Osulog
Проверить файл /usr/adm/Osulog и напечатать записи о нарушителях в стандартный вывод.
В самом начале программы определяются и инициализируются все переменные и списки. В строке 14 определены два флага: alert (сигнал тревоги) и mail (почта). Они имеют значение TRUE или FALSE. В этой программе используются два файла: протокольный файл команды su, который вы выбираете, и временный файл. Поскольку мы собираемся применять некоторые подпрограммы стандартного ввода-вывода (stdio), мы пользуемся указателями на файлы fp1 и fp2, а не дескрипторами файлов. Применяется два буфера: один для зачитывания в него данных из протокольного файла команды su, а другой - для хранения имени временного файла. Первоначально именем протокольного файла является sulog. Если вместо него используется другое имя файла, то переменная log переустанавливается на это имя.
Затем инициализируются предопределенные списки разрешенных суперпользователей и администраторов. В нашем примере двумя разрешенными суперпользователями являются sage (sage-root) и root (root-root). Для того чтобы приспособить это для вашей системы, поместите здесь имена людей, которым вы хотите разрешить пользоваться командой su для превращения в суперпользователей. Список администраторов также должен содержать соответствующие имена. В данном примере в список администраторов входит одно имя - sage. Это значит, что sage будет единственным, кто получит почту, если указана почтовая опция.
Подразумеваемое состояние рассылки почты устанавливается в строке 23 на значение FALSE, т. е. почты нет. Это делается для того, чтобы после разбора командной строки переменная mail имела правильное значение.
Далее выполняется проверка на ошибки. Первая проверка выглядит несколько странной, но на самом деле вполне понятна. Переменная argc возвращает число аргументов командной строки, а argv указывает на массив, содержащий сами аргументы, каждый из которых сам является массивом символов. Если командная строка вообще имеет какие-либо аргументы (argc > 1) и первым символом первого аргумента (argv[1][0]) является дефис, то проверяется, корректная ли это опция. С целью проверки второго символа первого аргумента используется оператор case. Если аргументом является символ m, то флаг mail устанавливается в состояние TRUE, число аргументов (argc) уменьшается на единицу, а указатель на массив аргументов (argv) на единицу увеличивается. Это служит той же цели, что и удаление аргументов из командной строки в командных файлах интерпретатора shell. Напомним, что argv является в действительности массивом указателей, а массивы трактуются точно так же, как строки, с базовым адресом и смещением. Поэтому argv[0] == argv, и argv[1] == ++argv. Если опция отличается от -m, то в стандартный вывод печатается сообщение об ошибке и программа завершается. В строке 39 проверяется счетчик аргументов, чтобы понять, есть ли еще один аргумент. Напомним, что argv всегда на единицу отстает от argc, потому что само имя команды является первым аргументом массива. Следовательно, для того чтобы получить второй аргумент, мы должны перейти к следующей позиции (*++argv). Если аргумент имеется, он должен быть именем протокольного файла, и это имя заносится в переменную log. Далее мы пытаемся открыть файлы, используемые в программе. Сначала открывается для чтения протокольный файл. Если это не срабатывает (возвращен нулевой указатель), то печатается сообщение об ошибке и программа завершается. Затем в строке 49 создается имя временного файла с добавлением к нему идентификатора процесса, чтобы гарантировать уникальность имени файла. Этот файл открывается на чтение и запись. Если эта операция не удается, печатается сообщение об ошибке и выполнение завершается.
В строках 57-103 заключен главный цикл. Управляющая часть главного цикла определяется тем, все ли данные прочитаны из протокольного файла. Если больше никакие байты прочитать нельзя, то fgets (строка 57) возвращает нулевой указатель, что завершает цикл while. Когда строка данных читается из протокольного файла, эти данные помещаются в массив line. Мы применяем указатель на символ для прохода вдоль строки и поиска записи о нарушителе.
Изучая запись в вашем файле sulog, вы можете увидеть, где находятся интересующие нас поля. Сначала указатель смещается на 15 символов от начала строки. Это позиция флага, определяющего, была ли успешной команда su. Если она была успешной, указатель увеличивается на два, чтобы пропустить пробел и установить указатель на имени терминала. Это имя имеет переменную длину, поэтому мы ориентируемся по пробелу в конце имени. В строках 63-64 применяется цикл while, который заставляет указатель пропустить все символы, отличные от пробела. Затем он увеличивается на единицу еще раз, чтобы пропустить пробел между именем терминала и строкой имени пользователя.
В этот момент указатель находится в последнем поле строки. Указатель на это поле помещается в переменную uname, чтобы затем ее можно было использовать для сравнения строк. Следующий цикл while (строки 66-67) проходит по строке, пока не попадет на символ дефиса. Цикл while выполняется до тех пор, пока указатель не указывает на конец строки (т.е. на нелевой указатель, *p == '\0') и еще не указывает на дефис. Поскольку в цикле используется p++, то когда мы попадаем на нулевой указатель, цикл завершается, причем p указывает на следующий символ.
Выполняется проверка, был ли суперпользователем тот, кто применил команду su. Если был, то взводится флаг alert, и мы должны проверить, разрешенный ли это суперпользователь. Цикл for (строки 72-77) пробегает по всем именам в массиве, содержащем имена разрешенных пользователей, до тех пор, пока мы не обнаружим последнюю запись, которая является пустой. Строка с именем пользователя, установленная предварительно, сверяется с каждой записью в массиве разрешенных имен. Если они совпадают, то мы можем предположить, что с данным применением команды su все в порядке. Тогда мы выключаем флаг alert и завершаем сравнение имен. Если же по окончании цикла флаг alert все еще взведен, значит имя не содержится в списке разрешенных - это, возможно, нарушитель. Вся запись целиком записывается во временный файл и управление передается назад в начало цикла для следующей операции чтения.
Когда все данные прочитаны, цикл while завершается. В строке 84 проверяется, взведен ли флаг mail. Если да, то временный файл закрывается (чтобы сработала команда cat) и выполняется еще один цикл for (строки 87-91). Он перебирает всех администраторов и завершается, когда попадает на нулевой указатель в конце массива. Для каждого из обозначенных администраторов конструируется команда mail и посылается в систему UNIX при помощи системного вызова (строка 90). Метод отправки по почте временного файла заключается в применении к этому файлу команды cat и передаче данных по конвейеру команде mail.
Если флаг mail выключен, то временный файл нужно напечатать в стандартный вывод, а не отправить по почте. Для того чтобы сделать это как можно быстрее, временный файл (который пока еще открыт) "перематывается" (мы позиционируемся в его начало) и посимвольно пропускается по циклу. Обратите внимание, что данный цикл является сердцевиной команды cat, как описано на странице 153 книги Кернигана и Ритчи "Язык программирования Си" (B.W.Kernighan, D.M.Ritchie. The C Programming Language). После того как временный файл напечатан, он закрывается. Наконец, закрывается протокольный файл и удаляется временный файл.
Назад | Содержание | Вперед