fixed torrent file path vulnerability

This commit is contained in:
Arvid Norberg 2009-05-28 07:25:09 +00:00
parent d5c8cd5274
commit eb2203abf5
3 changed files with 47 additions and 33 deletions

View File

@ -71,6 +71,7 @@ release 0.14.4
* improved handling of out-of-memory conditions in disk I/O thread * improved handling of out-of-memory conditions in disk I/O thread
* fixed bug when force-checking a torrent with partial pieces * fixed bug when force-checking a torrent with partial pieces
* fixed memory leak in disk cache * fixed memory leak in disk cache
* fixed torrent file path vulnerability
release 0.14.3 release 0.14.3

View File

@ -163,6 +163,16 @@ namespace libtorrent
if (!verify_encoding(p)) target.path = p; if (!verify_encoding(p)) target.path = p;
} }
bool valid_path_element(std::string const& element)
{
if (element.empty()
|| element == "." || element == ".."
|| element[0] == '/' || element[0] == '\\'
|| element[element.size()-1] == ':')
return false;
return true;
}
void trim_path_element(std::string& path_element) void trim_path_element(std::string& path_element)
{ {
#ifdef FILENAME_MAX #ifdef FILENAME_MAX
@ -189,6 +199,20 @@ namespace libtorrent
} }
} }
fs::path sanitize_path(fs::path const& p)
{
fs::path new_path;
for (fs::path::const_iterator i = p.begin(); i != p.end(); ++i)
{
if (!valid_path_element(*i)) continue;
std::string pe = *i;
trim_path_element(pe);
new_path /= pe;
}
TORRENT_ASSERT(!new_path.is_complete());
return new_path;
}
bool extract_single_file(lazy_entry const& dict, file_entry& target bool extract_single_file(lazy_entry const& dict, file_entry& target
, std::string const& root_dir) , std::string const& root_dir)
{ {
@ -215,12 +239,11 @@ namespace libtorrent
return false; return false;
std::string path_element = p->list_at(i)->string_value(); std::string path_element = p->list_at(i)->string_value();
trim_path_element(path_element); trim_path_element(path_element);
if (path_element != "..") target.path /= path_element;
target.path /= path_element;
} }
target.path = sanitize_path(target.path);
verify_encoding(target); verify_encoding(target);
if (target.path.is_complete()) TORRENT_ASSERT(!target.path.is_complete());
return false;
// bitcomet pad file // bitcomet pad file
if (target.path.string().find("_____padding_file_") != std::string::npos) if (target.path.string().find("_____padding_file_") != std::string::npos)
@ -568,34 +591,9 @@ namespace libtorrent
return false; return false;
} }
fs::path tmp = name; name = sanitize_path(name).string();
if (tmp.is_complete())
{ if (!valid_path_element(name))
name = tmp.leaf();
trim_path_element(name);
}
#if BOOST_VERSION < 103600
else if (tmp.has_branch_path())
#else
else if (tmp.has_parent_path())
#endif
{
fs::path p;
for (fs::path::iterator i = tmp.begin()
, end(tmp.end()); i != end; ++i)
{
if (*i == "." || *i == "..") continue;
std::string path_element = *i;
trim_path_element(path_element);
p /= path_element;
}
name = p.string();
}
else
{
trim_path_element(name);
}
if (name == ".." || name == ".")
{ {
ec = error_code(errors::torrent_invalid_name, libtorrent_category); ec = error_code(errors::torrent_invalid_name, libtorrent_category);
return false; return false;

View File

@ -55,6 +55,10 @@ using namespace libtorrent;
using namespace boost::tuples; using namespace boost::tuples;
using boost::bind; using boost::bind;
namespace libtorrent {
fs::path sanitize_path(fs::path const& p);
}
sha1_hash to_hash(char const* s) sha1_hash to_hash(char const* s)
{ {
sha1_hash ret; sha1_hash ret;
@ -355,6 +359,17 @@ int test_main()
{ {
using namespace libtorrent; using namespace libtorrent;
TEST_CHECK(sanitize_path("/a/b/c").string() == "a/b/c");
TEST_CHECK(sanitize_path("a/../c").string() == "a/c");
TEST_CHECK(sanitize_path("/.././c").string() == "c");
TEST_CHECK(sanitize_path("dev:").string() == "");
TEST_CHECK(sanitize_path("c:/b").string() == "b");
#ifdef TORRENT_WINDOWS
TEST_CHECK(sanitize_path("c:\\.\\c").string() == "c");
#else
TEST_CHECK(sanitize_path("//./c").string() == "c");
#endif
// make sure the time classes have correct semantics // make sure the time classes have correct semantics
TEST_CHECK(total_milliseconds(milliseconds(100)) == 100); TEST_CHECK(total_milliseconds(milliseconds(100)) == 100);
@ -690,7 +705,7 @@ int test_main()
torrent["info"] = info; torrent["info"] = info;
torrent_info ti2(torrent); torrent_info ti2(torrent);
std::cerr << ti2.name() << std::endl; std::cerr << ti2.name() << std::endl;
TEST_CHECK(ti2.name() == "test3"); TEST_CHECK(ti2.name() == "test1/test2/test3");
info["name.utf-8"] = "test2/../test3/.././../../test4"; info["name.utf-8"] = "test2/../test3/.././../../test4";
torrent["info"] = info; torrent["info"] = info;