Add openat/unlinkat/etc. abstractions to ReadDir/DirEntry/OpenOptions · Issue #259 · rust-lang/libs-team (original) (raw)

Proposal

Problem statement

Current std::fs APIs lead to code that's vulnerable to TOCTOU issues because performing any operation relative to a directory currently has to be done by composing the directory's path and the relative path. This happens because the directory structure can change under the user's nose between enumerating the directory entries and then trying to open/create/delete/... a file in that directory.

cap_std::fs::Dir by @sunfishcode already solves this.

Motivating examples or use cases

CVE-2022-21658, CVE-2018-15664, CVE-2021-20316 and similar symlink-TOCTOUs in many applications.

Solution sketch

This is more or less adopting fs::{Dir, DirEntry} APIs or a subset thereof into std.
It may be possible to add the Dir methods directly to ReadDir instead.

impl Dir/ReadDir { pub fn open<P: AsRef>(&self, path: P) -> Result /// This could be put on OpenOptions instead pub fn open_with<P: AsRef>(&self, path: P, options: &OpenOptions) -> Result pub fn create_dir<P: AsRef>(&self, path: P) -> Result<()> pub fn rename<P: AsRef, Q: AsRef>(&self, from: P, to_dir: &Self, to: Q) -> Result<()> pub fn remove_file<P: AsRef>(&self, path: P) -> Result<()> pub fn remove_dir<P: AsRef>(&self, path: P) -> Result<()> pub fn symlink<P: AsRef, Q: AsRef>(&self, original: P, link: Q)

 /// ... more convenience methods

}

impl DirEntry { pub fn open(&self) -> Result /// This could be put on OpenOptions instead pub fn open_with(&self, options: &OpenOptions) -> Result pub fn remove_file(&self) -> Result<()> pub fn remove_dir(&self) -> Result<()> }

The implementation work can be done piecemeal:

Open Questions

How do we deal with windows? NtCreateFile is considered internal. We're already using it to make remove_dir_all robust against races but in principle we could revert to simpler implementation. Adding public APIs that require it would be a foward-compatibility hazard.

How do we handle platforms that lack some of the necessary APIs? Emulate them via path manipulation (which reintroduces the TOCTOU) or return Unsupported errors?

Alternatives

Status Quo

Crates handling this already exist, we can tell users to use them in security-sensitive contexts.

IO-safety APIs for ReadDir

This would simplify using std::ReadDir as a directory handle and then passing it to crates like cap_std or openat

That would adding From<OwnedFd>, Into<OwnedFd>, AsFd (and windows equivalents) to ReadDir.

Issues: