Тео де Раадт придумал, как сделать openat() реально безопасным, потому что оказалось, ч...
Тео де Раадт придумал, как сделать openat() реально безопасным, потому что оказалось, что он таким не был Jказывается, замена open() на openat() сама по себе ничего не дает с точки зрения безопасности. Если передать в openat() абсолютный путь вроде "/etc/hosts", функция просто проигнорирует переданный dirfd и откроет файл как обычно, полностью игнорируя попытку "привязать" доступ к конкретному каталогу. То есть вся эта конструкция с openat(), которую многие программисты годами считали защитным механизмом от directory traversal, на деле защищает ровно ни от чего, если разработчик явно не добавил проверки сам. Де Раадт уперся в эту проблему практически — при разработке openrsync понадобилось жестко ограничить программу от выхода за пределы рабочего каталога, а стандартные unveil() и pledge() для этой задачи не годились. Предложение получилось элегантным: флаг F_BELOW для fcntl() (или O_BELOW для open()), который делает ограничение частью самого файлового дескриптора, а не ответственностью программиста на каждый вызов. Если dirfd помечен таким флагом, любая попытка уйти через ".." или абсолютный путь просто упадет с ENOENT. Разница принципиальная - вместо того, чтобы полагаться на внимательность разработчика, который должен не забыть проверку на каждом из десятков мест в коде, ограничение зашивается прямо в дескриптор на уровне ядра. Отдельно порадовал пассаж про Linux-аналоги RESOLVE_BENEATH и RESOLVE_IN_ROOT для openat2 — де Раадт прямым текстом указал, что эти флаги страдают той же проблемой: их нужно добавлять вручную ко всем нужным вызовам, а в случае захвата процесса атакующий всегда найдет другой способ открыть файл в обход этих проверок. Патчи пока на стадии обсуждения и даже не попали в -current, так что до реального релиза пройдет не один месяц традиционной неспешной процедуры ревью сообщества. @linux0ids