Избегайте состояния TOCTOU (время проверки, время использования) между статистикой и переименованием

Как избежать состояния гонки TOCTOU (время проверки, время использования) для состояния гонки между статистикой и переименованием для LOGFILE?

Требуется для перемещения файла журнала после того, как его значение размера превысит максимальный размер.

result = stat(LOGFILE, & data);
if (result != 0) {
  // stat failed
  // file probably does not exist
} else if (data.st_size > MAX_LOGSIZE) {
  unlink(PREV_LOGFILE);
  (void) rename(LOGFILE, PREV_LOGFILE);
}

person Amruth A    schedule 27.06.2019    source источник
comment
откройте файл и используйте fstat, чтобы файл, которым вы манипулируете, не мог измениться   -  person OznOg    schedule 27.06.2019
comment
Файл @Oz должен быть открыт всегда..   -  person Amruth A    schedule 27.06.2019
comment
Почему это вообще проблема? Вы пишете в файл журнала из другого потока? Тогда вам, вероятно, следует использовать мьютекс. В любом случае он не будет уменьшаться, поэтому успешная проверка никогда не будет аннулирована.   -  person n. 1.8e9-where's-my-share m.    schedule 27.06.2019
comment
@n.m да, другая ветка, это указывает на проблему сокрытия   -  person Amruth A    schedule 27.06.2019
comment
Это ложное срабатывание.   -  person n. 1.8e9-where's-my-share m.    schedule 27.06.2019
comment
@n.m как этого избежать? любая идея, где искать.   -  person Amruth A    schedule 27.06.2019
comment
Нет проблем, которых следует избегать.   -  person n. 1.8e9-where's-my-share m.    schedule 27.06.2019
comment
@n.m: Файловая система TOCTTOU не связана с гонками в рамках одного процесса. Речь идет о злоумышленнике в вашей системе, манипулирующем файловой системой, пока вы пытаетесь что-то сделать, с целью заставить вас сделать что-то, отличное от того, что вы намеревались. Обычный случай — это какая-то проверка контроля доступа, за которой следует операция записи: злоумышленник позволяет вам выполнить проверку доступа к правильному файлу, а затем заменяет его ссылкой на какой-то другой файл, к которому вы можете получить доступ, но злоумышленник не может .   -  person user3553031    schedule 27.06.2019
comment
'да другой поток'действительно поток или, возможно, скорее процесс? Если разные потоки внутри одного процесса, есть более простые способы решения проблем, например. грамм. все основано на объекте std::ofstream — в любом случае вам нужно будет защитить все это с помощью объекта std::mutex. В любом случае вам это нужно, если в файл журнала будет записываться более одного потока...   -  person Aconcagua    schedule 27.06.2019
comment
@user3553031 user3553031 о злоумышленнике в вашей системе. Мы говорим о заданном вопросе или о TOCTTOU, связанном с файловой системой в целом? Я не собираюсь обсуждать второй, потому что он явно не по теме. Что касается заданного вопроса, нет риска, что злоумышленник манипулирует размером вашего файла, чтобы заставить вас переключать журналы, потому что злоумышленник с теми же разрешениями может легко нанести больший ущерб напрямую, не заморачиваясь с TOCTTOU.   -  person n. 1.8e9-where's-my-share m.    schedule 27.06.2019
comment
@n.m: предположим, что LOGFILE — это «/path/to/file.log». Злоумышленник, имеющий доступ для записи к /path, но не к /path/to, может изменить ссылку, чтобы она указывала на какой-либо другой каталог, что приведет к удалению жертвой неправильный файл. Да, это немного натянуто, но я не вижу здесь более привлекательных сценариев атаки. TOCTTOU в основном беспокоит, когда принимаются решения по управлению доступом и проверке достоверности ввода.   -  person user3553031    schedule 27.06.2019
comment
@ user3553031 TOCTTOU эксплуатируется путем внедрения вредоносного действия между проверкой и использованием. В данном случае это не имеет никакого смысла. Все, что может сделать этот конкретный злоумышленник, он также может сделать до проверки и/или после использования.   -  person n. 1.8e9-where's-my-share m.    schedule 27.06.2019


Ответы (1)


Стандартный способ избежать TOCTTOU при операциях с файлами – open файл один раз, а затем выполнить все необходимые действия с помощью файлового дескриптора, а не имени файла.

Однако как для переименования, так и для удаления связи с файлом требуется его путь (поскольку им нужно знать, какую ссылку переименовывать или удалять), поэтому здесь вы не можете использовать этот подход. Альтернативой может быть копирование содержимого файла в другое место, а затем его усечение до нуля байтов, хотя ваш сценарий с файлами журналов, вероятно, требует, чтобы операция была атомарной, что может быть трудновыполнимым. Другой подход заключается в требовании жесткого контроля доступа к каталогу: если злоумышленник не может писать в каталог, он не может играть в игры TOCTTOU с вашим процессом. Вы можете использовать unlinkat и renameat, чтобы ограничить свои пути файловым дескриптором определенного каталога, чтобы вам не нужно было беспокоиться об изменении самого каталога.

Что-то вроде этого непроверенного кода может выполнить эту работу, предполагая платформу, подобную POSIX:

dirfd = open(LOGDIR, O_DIRECTORY);
// check for failure
res = fstatat(dirfd, LOGFILE, statbuf, AT_SYMLINK_NOFOLLOW);
if ((0 == res) && (S_ISREG(statbuf) && (data.st_size > MAX_LOGSIZE)) {
    unlinkat(dirfd, PREV_LOGFILE, 0);
    renameat(dirfd, LOGFILE, dirfd, PREV_LOGFILE);
}
close(dirfd);
person user3553031    schedule 27.06.2019