#open #chroot #secure

obnth

Allows securely opening files in untrusted directories on *nix systems

2 releases

0.1.1 Feb 5, 2021
0.1.0 Jan 3, 2021

#1187 in Filesystem

MIT license

91KB
2K SLoC

obnth

crates.io Docs GitHub Actions Cirrus CI codecov

Allows securely opening files in untrusted directories on *nix systems.

WARNING: This crate is not production-ready yet. You may be interested in using libpathrs instead.


lib.rs:

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).

Dependencies

~150KB