Merge pull request #9759 from Techjar/netplay-sanitize-gci

NetPlay: Check file names when receiving GCI folder
This commit is contained in:
JosJuice 2021-05-31 19:46:26 +02:00 committed by GitHub
commit c404452d3e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 19 additions and 4 deletions

View File

@ -95,6 +95,13 @@ bool IsTitlePath(const std::string& path, std::optional<FromWhichRoot> from, u64
return true; return true;
} }
static bool IsIllegalCharacter(char c)
{
static const std::unordered_set<char> illegal_chars = {'\"', '*', '/', ':', '<',
'>', '?', '\\', '|', '\x7f'};
return (c >= 0 && c <= 0x1F) || illegal_chars.find(c) != illegal_chars.end();
}
std::string EscapeFileName(const std::string& filename) std::string EscapeFileName(const std::string& filename)
{ {
// Prevent paths from containing special names like ., .., ..., ...., and so on // Prevent paths from containing special names like ., .., ..., ...., and so on
@ -105,13 +112,11 @@ std::string EscapeFileName(const std::string& filename)
std::string filename_with_escaped_double_underscores = ReplaceAll(filename, "__", "__5f____5f__"); std::string filename_with_escaped_double_underscores = ReplaceAll(filename, "__", "__5f____5f__");
// Escape all other characters that need to be escaped // Escape all other characters that need to be escaped
static const std::unordered_set<char> chars_to_replace = {'\"', '*', '/', ':', '<',
'>', '?', '\\', '|', '\x7f'};
std::string result; std::string result;
result.reserve(filename_with_escaped_double_underscores.size()); result.reserve(filename_with_escaped_double_underscores.size());
for (char c : filename_with_escaped_double_underscores) for (char c : filename_with_escaped_double_underscores)
{ {
if ((c >= 0 && c <= 0x1F) || chars_to_replace.find(c) != chars_to_replace.end()) if (IsIllegalCharacter(c))
result.append(fmt::format("__{:02x}__", c)); result.append(fmt::format("__{:02x}__", c));
else else
result.push_back(c); result.push_back(c);
@ -151,4 +156,11 @@ std::string UnescapeFileName(const std::string& filename)
return result; return result;
} }
bool IsFileNameSafe(const std::string_view filename)
{
return !filename.empty() &&
!std::all_of(filename.begin(), filename.end(), [](char c) { return c == '.'; }) &&
std::none_of(filename.begin(), filename.end(), IsIllegalCharacter);
}
} // namespace Common } // namespace Common

View File

@ -43,4 +43,6 @@ std::string EscapeFileName(const std::string& filename);
std::string EscapePath(const std::string& path); std::string EscapePath(const std::string& path);
// Reverses escaping done by EscapeFileName // Reverses escaping done by EscapeFileName
std::string UnescapeFileName(const std::string& filename); std::string UnescapeFileName(const std::string& filename);
// Tests for a file name being "safe" as per the escaping defined in EscapeFileName
bool IsFileNameSafe(const std::string_view filename);
} // namespace Common } // namespace Common

View File

@ -905,7 +905,8 @@ unsigned int NetPlayClient::OnData(sf::Packet& packet)
std::string file_name; std::string file_name;
packet >> file_name; packet >> file_name;
if (!DecompressPacketIntoFile(packet, path + DIR_SEP + file_name)) if (!Common::IsFileNameSafe(file_name) ||
!DecompressPacketIntoFile(packet, path + DIR_SEP + file_name))
{ {
SyncSaveDataResponse(false); SyncSaveDataResponse(false);
return 0; return 0;