и для нее установлен бит SUID. Обратите внимание на то, что команда
su
не меняет идентификатор интерпретатора команд, в котором она была вызвана, а запускает новый интерпретатор с измененным идентификатором. Первоначальный интерпретатор будет заблокирован до тех пор, пока пользователь не введет
exit
.
10.5. Аутентификация пользователей
Программы, у которых установлен бит SUID, не должны запускаться кем попало.
Например, программа
su
, прежде чем менять идентификатор пользователя, заставляет его ввести пароль. Это называется аутентификацией — программа проверяет, получил ли пользователь права суперпользователя.
Администраторам высоконадежных систем недостаточно, чтобы пользователи просто вводили пароли. У пользователей есть вредная привычка записывать свои пароли на бумажке, приклеенной к монитору, или выбирать пароли, в которых закодирован день рождения, имя любимой собаки, жены и т.п. Все это облегчает задачу злоумышленникам, пытающимся незаконно проникнуть в систему.
Во многих организациях требуется использовать "одноразовые" пароли, генерируемые специальными электронными карточками, которые пользователи хранят при себе. Одни и тот же пароль не может встретиться дважды, а прежде чем получить пароль, требуется ввести личный код. Следовательно, для взлома системы хакеру требуется раздобыть электронную карточку и узнать соответствующий личный код. В сверхсекретных учреждениях используются устройства сканирования сетчатки глаза или другие биометрические приборы.
При написании аутентификационной программы важно позволить системному администратору использовать тот механизм аутентификации, который он считает приемлемым. В Linux этой цели служат подключаемые модули аутентификации (РАМ, pluggable authentication modules). Рассмотрим простейшее приложение (листинг 10.4).
Листинг 10.4. (pam.c) Пример использования модулей РАМ
#include <security/pam_appl.h>
#include <security/pam_misc.h>
#include <stdio.h>
int main {
pam_handle_t* pamh;
struct pam_conv pamc;
/* Указание диалоговой функции. */
pamc.conv = &misc_conv;
pamc.eppdata_ptr = NULL;
/* Начало сеанса аутентификации. */
pam_start("su", getenv("USER"), &pamc, &pamh);
/* Аутентификация пользователя. */
if (pam_authenticate(pamh, 0) != PAM_SUCCESS)
fprintf(stderr, "Authentication failed!\n");
else
fprintf(stderr, "Authentication OK.\n");
/* Конец сеанса. */
pam_end(pamh, 0);
return 0;
}
Чтобы скомпилировать эту программу, необходимо подключить к ней две библиотеки:
libpam
и
libpam_misс
:
% gcc -о para pam.c -lpam -lpam_misc
Сначала программа создает объект диалога, который используется библиотекой РАМ, когда ей требуется запросить у пользователя данные. Функция
misc_conv
, адрес которой записывается в объект, — это стандартная диалоговая функция, осуществляющая терминальный ввод-вывод. Можно написать собственную функцию, отображающую всплывающее окно, использующую голосовой ввод-вывод или реализующую другие способы общения с пользователем.
Затем вызывается функция
pam_start
, которая инициализирует библиотеку
РАМ. Первый аргумент функции — это имя сервиса. Оно должно уникальным образом идентифицировать приложение. Программа не будет работать, пока системный администратор не настроит систему на использование указанного сервиса. В данном случае задействуется сервис
su
, при котором программа аутентифицирует пользователей так же, как это делает команда
su
. В реальных программах так поступать не следует. Выберите реальное имя сервиса и создайте сценарий инсталляции, который позволит системному администратору правильно настраивать механизм аутентификации.
Второй аргумент функции — это имя пользователя, которого требуется аутентифицировать. В данном примере имя пользователя берется из переменной среды
USER
(обычно это имя соответствует эффективному идентификатору текущего процесса, но так бывает не всегда). В большинстве реальных программ в данном месте выдается запрос на ввод имени. Третьим аргументом является ссылка на объект диалога. В четвертом аргументе указывается переменная, в которую функция
pam_start
запишет дескриптор сеанса. Этот дескриптор должен передаваться всем последующим функциям библиотеки РАМ.
Далее в программе вызывается функция
pam_authenticate
. Во втором ее аргументе указываются различные флаги. Значение 0 означает стандартные установки. Возвращаемое значение функции говорит о том. как прошла аутентификация. В конце программы вызывается функция
pam_end
, которая удаляет выделенные ранее структуры данных.
Предположим, что пользователь должен ввести пароль "password". Если это будет сделано, получим следующее:
% ./pam
Password: password
Authentication OK.
Будучи запущенной в терминальном окне, программа не покажет введенный пароль, чтобы кто-нибудь посторонний не смог его подглядеть.
Вот что произойдет, если в систему попробует вломиться хакер:
% ./pam
Password: badguess
Authentication failed!
Полное описание работы модулей аутентификации приведено в каталоге
/usr/doc/pam
.
10.6. Дополнительные проблемы безопасности
В этой главе мы рассматриваем лишь несколько наиболее общих проблем, связанных с безопасностью. Но существует великое множество других "дыр", и далеко не все из них еще раскрыты. Поэтому в ответственных случаях без помощи экспертов не обойтись.
10.6.1. Переполнение буфера
Почти псе основные Internet-демоны, включая демоны таких программ, как
sendmail
,
finger
,
talk
и др., подвержены атакам типа переполнение буфера. О них следует обязательно помнить при написании программ, которые должны выполняться с правами пользователя root, а также программ, осуществляющих межзадачное взаимодействие или читающих файлы, которые не принадлежат пользователю, запустившему программу.
Суть атаки заключается в том, чтобы заставить программу выполнить код, который она не собиралась выполнять. Типичный механизм достижения этой цели — перезапись части стека программы. В стеке, помимо всего прочего, сохраняется адрес памяти, по которому программа передает управление после завершения текущей функции. Следовательно, если поместить код взлома в памяти, а затем изменить адрес возврата так. чтобы он указывал на этот код, то по завершении текущей функции программа начнет выполнять код хакера с правами текущего процесса. Если процесс принадлежит пользователю
root
, последствия будут катастрофическими. Если атаке подвергся процесс другого пользователя, катастрофа наступит "только" для него (а также для любого пользователя, который работает с файлами пострадавшего).