Skip to content

Commit 692b5ea

Browse files
authored
Merge pull request #2519 from pobregat0/torrent-path-check
check names from a torrent file before they become a path
2 parents 58cd483 + 850bccd commit 692b5ea

2 files changed

Lines changed: 42 additions & 1 deletion

File tree

libi2pd_client/Torrents.cpp

Lines changed: 41 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
#include <fstream>
1414
#include <sstream>
1515
#include <algorithm>
16+
#include <array>
1617
#include <functional>
1718
#include <set>
1819
#include <boost/algorithm/string.hpp>
@@ -335,6 +336,7 @@ namespace torrents
335336
return len;
336337
}
337338

339+
338340
size_t Torrent::ParseInfo (std::string_view buf)
339341
{
340342
size_t len = ParseDictionary (buf, [this](std::string_view key, std::string_view buf)->size_t
@@ -348,7 +350,15 @@ namespace torrents
348350
else if (key == "name")
349351
{
350352
auto [name, l] = ExtractByteString (buf);
351-
if (l) m_Name = name;
353+
if (l)
354+
{
355+
if (!IsSafeName (name))
356+
{
357+
LogPrint (eLogError, "Torrents: Unsafe name in torrent: ", name);
358+
return 0;
359+
}
360+
m_Name = name;
361+
}
352362
return l;
353363
}
354364
else if (key == "piece length")
@@ -373,6 +383,29 @@ namespace torrents
373383
return len;
374384
}
375385

386+
bool Torrent::IsSafeName (std::string_view name)
387+
{
388+
// names come from a torrent file, that is from a stranger. A component
389+
// that is empty, a dot pair, absolute or separated would take the path
390+
// out of the torrents folder, and some names are refused by Windows
391+
if (name.empty () || name == "." || name == "..") return false;
392+
if (name.back () == '.' || name.back () == ' ') return false; // Windows drops those
393+
for (char ch: name)
394+
if (ch == '/' || ch == '\\' || ch == ':' || ch == '<' || ch == '>' ||
395+
ch == '"' || ch == '|' || ch == '?' || ch == '*' || (unsigned char)ch < 0x20)
396+
return false;
397+
using namespace std::string_view_literals;
398+
static constexpr std::array reserved
399+
{
400+
"CON"sv, "PRN"sv, "AUX"sv, "NUL"sv,
401+
"COM1"sv, "COM2"sv, "COM3"sv, "COM4"sv, "COM5"sv, "COM6"sv, "COM7"sv, "COM8"sv, "COM9"sv,
402+
"LPT1"sv, "LPT2"sv, "LPT3"sv, "LPT4"sv, "LPT5"sv, "LPT6"sv, "LPT7"sv, "LPT8"sv, "LPT9"sv
403+
};
404+
std::string stem (name.substr (0, name.find ('.')));
405+
boost::to_upper (stem);
406+
return std::find (reserved.begin (), reserved.end (), stem) == reserved.end ();
407+
}
408+
376409
size_t Torrent::ParseFiles (std::string_view buf)
377410
{
378411
m_Length = 0;
@@ -386,7 +419,14 @@ namespace torrents
386419
auto [subdirs, l] = ParseStringList (value);
387420
if (l)
388421
for (const auto& it: subdirs)
422+
{
423+
if (!IsSafeName (it))
424+
{
425+
LogPrint (eLogError, "Torrents: Unsafe path component in torrent: ", it);
426+
return 0;
427+
}
389428
filePath /= it;
429+
}
390430
return l;
391431
}
392432
else if (key == "length")

libi2pd_client/Torrents.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -220,6 +220,7 @@ namespace torrents
220220
size_t ParseInfo (std::string_view buf);
221221
size_t ParsePeers (size_t trackerID, std::string_view buf);
222222
size_t ParseFiles (std::string_view buf);
223+
static bool IsSafeName (std::string_view name); // a name from a torrent file before it becomes a path
223224

224225
private:
225226

0 commit comments

Comments
 (0)