1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
//! # 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`:
//! ```no_run
//! # use obnth::Dir;
//! 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:
//! ```no_run
//! # use std::fs::File;
//! # use std::path::Path;
//! // 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:
//! ```no_run
//! # use std::fs::File;
//! # use std::path::Path;
//! // 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).

mod as_path;
mod constants;
mod dir;
mod open;
mod sys;
mod util;

pub use as_path::*;
pub use dir::*;
pub use open::*;