Kniga-Online.club
» » » » Марк Митчелл - Программирование для Linux. Профессиональный подход

Марк Митчелл - Программирование для Linux. Профессиональный подход

Читать бесплатно Марк Митчелл - Программирование для Linux. Профессиональный подход. Жанр: Программирование издательство -, год 2004. Так же читаем полные версии (весь текст) онлайн без регистрации и SMS на сайте kniga-online.club или прочесть краткое содержание, предисловие (аннотацию), описание и ознакомиться с отзывами (комментариями) о произведении.
Перейти на страницу:

В листинге 10.5 представлена функция, которая пытается безопасно открыть файл в каталоге /tmp. Возможно, у этой функции есть свои слабости. Мы не рекомендуем читателям включать показанный код в свои программы без дополнительной экспертизы, просто мы хотим убедить читателей в том, что создание безопасных приложений — непростая задача,

Листинг 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-бита.

10.6.3. Функции system() и popen()

Третья распространенная проблема безопасности, о которой должен помнить каждый программист, заключается в несанкционированном запуске программ через интерпретатор команд. В качестве наглядной демонстрации рассмотрим сервер словарей. Серверная программа ожидает поступления запросов через Internet. Клиент посылает слово, а сервер сообщает, является ли оно корректным словом английского языка. В любой Linux-системе имеется файл /usr/dict/words, в котором содержится список 45000 слов, поэтому серверу достаточно выполнить такую команду:

% grep -х слово /usr/dict/words

Код завершения команды grep сообщит о том, обнаружено ли указанное слово в файле /usr/dict/words.

В листинге 10.6 показан пример реализации поискового модуля сервера.

Листинг 10.6. (grep-dictionary.c) Поиск слова в словаре

#include <stdio.h>

#include <stdlib.h>

/* Функция возвращает ненулевое значение, если аргумент WORD

   встречается в файле /usr/dict/words. */

int grep_for_word(const char* word) {

 size_t length;

 char* buffer;

 int exit_code;

 /* Формирование строки 'grep -x WORD /usr/dict/words'.

    Строка выделяется динамически во избежание

    переполнения буфера. */

 length =

  strlen("grep -х ") + strlen(word) +

 strlen(" /usr/dict/words") + 1;

 buffer = (char*)malloc(length);

 sprintf(buffer, "grep -x %s /usr/dict/words", word);

 /* Запуск команды. */

 exit_code = system(buffer);

 /* Очистка буфера. */

 free(buffer);

 /* Если команда grep вернула значение 0, значит, слово найдено

    в словаре. */

 return exit_code == 0;

}

Обратите внимание на подсчет числа символов в строке и динамическое выделение буфера, что позволяет обезопасить программу от переполнения буфера. К сожалению, небезопасна сама функция system() (описана в разделе 3.2.1, "Функция system()"). Функция вызывает стандартный интерпретатор команд и принимает от него код завершения. Но что произойдет, если злоумышленник вместо слова введет показанную ниже строку?

foo /dev/null; rm -rf /

В этом случае сервер выполнит такую команду:

grep -х foo /dev/null; rm -rf / /usr/dict/words

Теперь проблема стала очевидной. Пользователь запустил одну команду, якобы grep, а на самом деле их оказалось две, так как интерпретатор считает точку с запятой разделителем команд. Первая команда — это по-прежнему безобидный вызов утилиты grep, зато вторая команда пытается удалить все файлы в системе. Даже если серверная программа не имеет привилегий суперпользователя, она удалит все файлы, доступные запустившему ее пользователю. Похожая проблема возникает и при использовании функции popen() (описана в разделе 3.4.4, "Функции popen() и pclose()"), которая создает канал между родительским и дочерним процессами, но тоже вызывает интерпретатор для запуска команды.

Существуют два способа устранения подобных проблем. Первый заключается в использовании функции семейства exec() вместо функции system() или popen(). Специальные символы интерпретатора команд (например, точка с запятой) не подвергаются обработке, если они присутствуют в списке аргументов функции exec(). Естественно, при этом пропадают преимущества таких функций, как system() и popen().

Второй способ — проверка строки на предмет "благонадежности". В случае сервера словарей следует убедиться в том, что слово содержит только буквы (для этого предназначена функция isalpha()). Такое слово не представляет угрозы.

Глава 11

Демонстрационное Linux-приложение

В этой главе кусочки мозаики сложатся в единую композицию. Мы опишем и реализуем законченную Linux-программа, в которой объединятся многие рассмотренные в данной книге методики. Программа через протокол HTTP выдает информацию о системе, в которой она работает.

11.1. Обзор

Демонстрационная программа является частью пакета мониторинга Linux-системы и предоставляет следующие возможности.

■ Программа реализует минимально необходимые функции Web-сервера. Локальные и удаленные клиенты получают доступ к системной информации, запрашивая Web-страницы у сервера по протоколу HTTP.

Перейти на страницу:

Марк Митчелл читать все книги автора по порядку

Марк Митчелл - все книги автора в одном месте читать по порядку полные версии на сайте онлайн библиотеки kniga-online.club.


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

Отзывы читателей о книге Программирование для Linux. Профессиональный подход, автор: Марк Митчелл. Читайте комментарии и мнения людей о произведении.


Уважаемые читатели и просто посетители нашей библиотеки! Просим Вас придерживаться определенных правил при комментировании литературных произведений.

  • 1. Просьба отказаться от дискриминационных высказываний. Мы защищаем право наших читателей свободно выражать свою точку зрения. Вместе с тем мы не терпим агрессии. На сайте запрещено оставлять комментарий, который содержит унизительные высказывания или призывы к насилию по отношению к отдельным лицам или группам людей на основании их расы, этнического происхождения, вероисповедания, недееспособности, пола, возраста, статуса ветерана, касты или сексуальной ориентации.
  • 2. Просьба отказаться от оскорблений, угроз и запугиваний.
  • 3. Просьба отказаться от нецензурной лексики.
  • 4. Просьба вести себя максимально корректно как по отношению к авторам, так и по отношению к другим читателям и их комментариям.

Надеемся на Ваше понимание и благоразумие. С уважением, администратор kniga-online.


Прокомментировать
Подтвердите что вы не робот:*
Подтвердите что вы не робот:*