Crate obnth[][src]

What is this crate useful for?

obnth makes it easy to safely open files in untrusted directories. This may be useful, for example, in web servers that serve files from user-controlled "webdocs" directories, or in set-UID programs that need to open files based on user-supplied information.

As a more concrete example, say you're serving files from /srv/user1:

let dir = Dir::open("/srv/user1").unwrap();

// If `a` and/or `a/index.html` are symlinks, they will be followed, but
// they can't escape `/srv/user1`.
let file = dir.open_file().open("a/index.html").unwrap();

Why is this dangerous? Can't I just use Path::join()?

A naive implementation of the above use case would just do something like this:

// DO NOT DO THIS
let file = File::open(Path::new("/srv/user1").join("a/index.html")).unwrap();

However, this is very dangerous. Just consider the case where /srv/user1/a/index.html is a symlink to, say, /etc/shadow. If the program in question is a web server, this essentially gives control of the system to anyone who can create a symlink in the target directory.

The next logical attempt is to use Path::canonicalize() and then validate the path before opening it:

// DO NOT DO THIS
let path = Path::new("/srv/user1").join("a/index.html").canonicalize().unwrap();
assert!(path.starts_with("/srv/user1"));
let file = File::open(path).unwrap();

However, between the call to Path::canonicalize() and the call to File::open(), an attacker may replace a/index.html with a symlink to another file, and still trick the program into opening files it shouldn't.

Why not use the openat or libpathrs crates?

  • openat provides friendly interfaces to the *at() functions. However, it doesn't perform any validation of the paths being provided, and you can very easily escape the directory by specifying absolute paths or paths containg .. components.

    openat is useful, for example, if you need to walk through a directory tree in a very controlled manner, and all you want is slightly higher-level interfaces to the *at() functions. However, if you just want to open a file within the given directory (and guarantee that it won't escape), obnth may be more useful.

  • libpathrs serves a similar role to this library, but it is very much Linux-specific. obnth works on Linux, macOS, and the BSDs (which necessitated certain differences in the semantics).

Structs

Dir

A wrapper around a directory file descriptor that allows opening files within that directory.

Entry

An entry encountered when iterating over a directory.

LookupFlags

Flags that modify path loookup when opening a file/directory beneath another directory.

Metadata

Represents metadata information about a file. Similar to std::fs::Metadata.

OpenOptions

A struct that can be used to open files within a directory.

ReadDirIter

An iterator over the entries of a directory.

Rename2Flags

Linux-specific: Flags for rename2().

SeekPos

Represents a seek position for a ReadDirIter struct.

Enums

FileType

Represents the possible file types.

Traits

AsPath

Represents a string that can be cheaply re-cast as a Path, and possibly also as a CStr.

Functions

hardlink

Create a hardlink to a file in (possibly) a different directory.

has_o_search

Returns true if this OS has an equivalent of the O_SEARCH flag, which allows opening files in certain extra cases.

open_beneath

Open a file beneath the specified directory.

rename

Rename a file across directories.

rename2

Linux-specific: Rename a file across directories, specifying extra flags to modify behavior.