/* Открытие устройства /dev/random. Предполагается, что
это устройство является источником случайных данных,
а не файлом, созданным хакером. */
random_fd = open("/dev/random", O_RDONLY);
/* Если устройство /dev/random не удалось открыть,
завершаем работу. */
if (random_fd == -1)
return -1;
}
/* чтение целого числа из устройства /dev/random. */
if (read(random_fd, &random, sizeof(random)) != sizeof(random))
return -1;
/* Формирование имени файла из случайного числа. */
sprintf(filename, "/tmp/%u", random);
/* Попытка открытия файла. */
fd = open(filename,
/* Используем флаг O_EXCL. */
O_RDWR | O_CREAT | O_EXCL,
/* Разрешаем доступ только владельцу файла. "/
S_IRUSR | S_IWUSR);
if (fd == -1)
return -1;
/* Вызываем функцию lstat(), чтобы проверить, не является ли
файл символической ссылкой */
if (lstat(filename, &stat_buf) == -1)
return -1;
/* Если файл не является обычным файлом, кто-то пытается
обмануть нас. */
if (!S_ISREG(stat_buf.st_mode))
return -1;
/* Если файл нам не принадлежит, то, возможно, кто-то успел
подменить его. */
if (stat_buf.st_uid != geteuid() ||
stat_buf.st_gid != getegid())
return -1;
/* Если у файла установлены дополнительные биты доступа,
что-то не так. */
if ((stat_buf.st_mode & ~(S_IRUSR | S_IWUSR)) != 0)
return -1;
return fd;
}
Рассмотренная функция вызывает функцию open()
lstat()
для проверки того, что файл не является символической ссылкой. Внимательный читатель обнаружит здесь то, что называется состоянием гонки. Между вызовами функций open()
и lstat()
злоумышленник может успеть удалить файл и заменить его символической ссылкой. Это не вызовет разрушающих последствий, но приведет к тому, что функция завершится ошибкой и не сможет выполнить свою задачу. Такой тип атаки называется В данной ситуации на помощь приходит sticky-бит. Поскольку для каталога /tmp
root
разрешено делать все что угодно, но если хакер сумел получить привилегии суперпользователя, вас уже ничто не спасет.Грамотный системный администратор не допустит, чтобы каталог /tmp
mkstemp()
. Если же речь идет о другом каталоге, то нельзя ни доверять флагу O_EXCL
, ни рассчитывать на установку sticky-бита.10.6.3. Функции system() и popen()
Третья распространенная проблема безопасности, о которой должен помнить каждый программист, заключается в несанкционированном запуске программ через интерпретатор команд. В качестве наглядной демонстрации рассмотрим сервер словарей. Серверная программа ожидает поступления запросов через Internet. Клиент посылает слово, а сервер сообщает, является ли оно корректным словом английского языка. В любой Linux-системе имеется файл /usr/dict/words
% grep -х слово /usr/dict/words
Код завершения команды grep
/usr/dict/words
.В листинге 10.6 показан пример реализации поискового модуля сервера.
#include
#include
/* Функция возвращает ненулевое значение, если аргумент WORD
встречается в файле /usr/dict/words. */
int grep_for_word(const char* word) {
size_t length;
char* buffer;
int exit_code;