Книга: Программирование для Linux. Профессиональный подход

Листинг 10.5. (temp-file.c) Безопасное создание временного файла

Листинг 10.5. (temp-file.c) Безопасное создание временного файла

#include <fcntl.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <unistd.h>
/* Функция возвращает дескриптор созданного временного файла.
   Файл будет доступен для чтения и записи только тому
   пользователю, чей идентификатор равен эффективному
   идентификатору текущего процесса. Если файл не удалось создать,
   возвращается -1. */
int secure_temp_file() {
 /* Этот дескриптор ссылается на устройство /dev/random, из
    которого будут получены случайные данные. */
 static int random_fd = -1;
 /* Случайное целое число. */
 unsigned int random;
 /* Буфер для преобразования числа в строку. */
 char filename[128];
 /* дескриптор создаваемого временного файла. */
 int fd;
 /* информация о созданном файле. */
 struct stat stat_buf;
 /* Если устройство /dev/random еще не было открыто,
    открываем его. */
 if (random_fd == -1) {
  /* Открытие устройства /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 был смонтирован через NFS, поэтому на практике можно пользоваться функцией mkstemp(). Если же речь идет о другом каталоге, то нельзя ни доверять флагу O_EXCL, ни рассчитывать на установку sticky-бита.

Оглавление книги


Генерация: 1.253. Запросов К БД/Cache: 3 / 1
поделиться
Вверх Вниз