Тео де Раадт придумал, как сделать 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, так что до реального релиза пройдет не один месяц традиционной неспешной процедуры ревью сообщества.
Linux / Линукс🥸
Post #12892
1.24K
- 👍 10
- ❤ 5