Skip to content

Commit

Permalink
Enforce a maximum symlink following depth
Browse files Browse the repository at this point in the history
Otherwise the thread that handles the request loops forever in a tight
readlink()/statx() 100% CPU loop

Minimal repro:
  ln -s tt tt
  http
and try to open :8000/tt
  • Loading branch information
nabijaczleweli committed Feb 17, 2022
1 parent bbf2f04 commit 46e7b80
Show file tree
Hide file tree
Showing 3 changed files with 22 additions and 8 deletions.
8 changes: 5 additions & 3 deletions src/ops/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,8 @@ use iron::{headers, status, method, mime, IronResult, Listening, Response, TypeM
use self::super::util::{WwwAuthenticate, DisplayThree, CommaList, Spaces, Dav, url_path, file_hash, is_symlink, encode_str, encode_file, file_length,
html_response, file_binary, client_mobile, percent_decode, escape_specials, file_icon_suffix, is_actually_file, is_descendant_of,
response_encoding, detect_file_as_dir, encoding_extension, file_time_modified, file_time_modified_p, get_raw_fs_metadata,
human_readable_size, encode_tail_if_trimmed, is_nonexistent_descendant_of, USER_AGENT, ERROR_HTML, INDEX_EXTENSIONS, MIN_ENCODING_GAIN,
MAX_ENCODING_SIZE, MIN_ENCODING_SIZE, DAV_LEVEL_1_METHODS, DIRECTORY_LISTING_HTML, MOBILE_DIRECTORY_LISTING_HTML,
human_readable_size, encode_tail_if_trimmed, is_nonexistent_descendant_of, USER_AGENT, ERROR_HTML, MAX_SYMLINKS, INDEX_EXTENSIONS,
MIN_ENCODING_GAIN, MAX_ENCODING_SIZE, MIN_ENCODING_SIZE, DAV_LEVEL_1_METHODS, DIRECTORY_LISTING_HTML, MOBILE_DIRECTORY_LISTING_HTML,
BLACKLISTED_ENCODING_EXTENSIONS};


Expand Down Expand Up @@ -1266,6 +1266,7 @@ impl HttpHandler {
}

fn parse_requested_path_custom_symlink(&self, req_url: &GenericUrl, follow_symlinks: bool) -> (PathBuf, bool, bool) {
let mut depth_left = MAX_SYMLINKS;
let (mut cur, sk, err, abs) = req_url.path_segments()
.unwrap()
.filter(|p| !p.is_empty())
Expand All @@ -1278,14 +1279,15 @@ impl HttpHandler {
}
while let Ok(newlink) = cur.read_link() {
sk = true;
if follow_symlinks {
if follow_symlinks && depth_left != 0 {
if newlink.is_absolute() {
cur = newlink;
} else {
abs = false;
cur.pop();
cur.push(newlink);
}
depth_left -= 1;
} else {
break;
}
Expand Down
17 changes: 12 additions & 5 deletions src/ops/webdav.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@
use self::super::super::util::{BorrowXmlName, Destination, CommaList, Overwrite, Depth, win32_file_attributes, file_time_accessed, file_time_modified,
file_time_created, client_microsoft, is_actually_file, is_descendant_of, file_executable, html_response, file_length, copy_dir,
WEBDAV_ALLPROP_PROPERTIES_NON_WINDOWS, WEBDAV_ALLPROP_PROPERTIES_WINDOWS, WEBDAV_XML_NAMESPACE_MICROSOFT,
WEBDAV_XML_NAMESPACE_APACHE, WEBDAV_PROPNAME_PROPERTIES, WEBDAV_XML_NAMESPACE_DAV, WEBDAV_XML_NAMESPACES, ERROR_HTML};
WEBDAV_XML_NAMESPACE_APACHE, WEBDAV_PROPNAME_PROPERTIES, WEBDAV_XML_NAMESPACE_DAV, WEBDAV_XML_NAMESPACES, MAX_SYMLINKS,
ERROR_HTML};
use std::io::{ErrorKind as IoErrorKind, Result as IoResult, Error as IoError, Write, Read};
use xml::reader::{EventReader as XmlReader, XmlEvent as XmlREvent, Error as XmlRError};
use xml::writer::{EventWriter as XmlWriter, XmlEvent as XmlWEvent, Error as XmlWError};
Expand Down Expand Up @@ -133,6 +134,7 @@ impl HttpHandler {
fn handle_webdav_propfind_path_recursive<'n, W: Write, N: BorrowXmlName<'n>>(&self, req: &mut Request, out: &mut XmlWriter<W>, root_url: String,
root_path: &Path, props: &[&'n [N]], just_names: bool, depth: Depth)
-> Result<Option<IronResult<Response>>, XmlWError> {
let mut links_left = MAX_SYMLINKS;
if let Some(next_depth) = depth.lower() {
for f in root_path.read_dir().expect("Failed to read requested directory").map(|p| p.expect("Failed to iterate over requested directory")) {
let mut url = root_url.clone();
Expand All @@ -145,11 +147,16 @@ impl HttpHandler {
let mut symlink = false;
while let Ok(newlink) = path.read_link() {
symlink = true;
if newlink.is_absolute() {
path = newlink;
if links_left != 0 {
if newlink.is_absolute() {
path = newlink;
} else {
path.pop();
path.push(newlink);
}
links_left -= 1;
} else {
path.pop();
path.push(newlink);
break;
}
}

Expand Down
5 changes: 5 additions & 0 deletions src/util/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,11 @@ pub const USER_AGENT: &str = concat!("http/", env!("CARGO_PKG_VERSION"));
/// Index file extensions to look for if `-i` was not specified and strippable extensions to look for if `-x` was specified.
pub const INDEX_EXTENSIONS: &[&str] = &["html", "htm", "shtml"];

/// Maximum amount of symlinks to follow in any given path lookup.
///
/// `40` matches the linux `MAXSYMLINKS` macro in `include/linux/namei.h`.
pub const MAX_SYMLINKS: usize = 40;


/// The [WWW-Authenticate header](https://tools.ietf.org/html/rfc7235#section-4.1), without parsing.
///
Expand Down

0 comments on commit 46e7b80

Please sign in to comment.