mirror of
https://github.com/scummvm/scummvm.git
synced 2026-06-20 05:45:29 +00:00
CLOUD: Handle paths in marked places
Paths containing '../' are forbidden to use in Files Manager. There is also a special inner black list of paths which are not used and a check that specified path is under "savepath" or "rootpath" (from "cloud" domain).
This commit is contained in:
@@ -61,6 +61,12 @@ void CreateDirectoryHandler::handle(Client &client) {
|
||||
return;
|
||||
}
|
||||
|
||||
// check that <path> contains no '../'
|
||||
if (HandlerUtils::hasForbiddenCombinations(path)) {
|
||||
handleError(client, _("Invalid path!"));
|
||||
return;
|
||||
}
|
||||
|
||||
// transform virtual path to actual file system one
|
||||
Common::String prefixToRemove = "", prefixToAdd = "";
|
||||
if (!transformPath(path, prefixToRemove, prefixToAdd) || path.empty()) {
|
||||
@@ -68,10 +74,12 @@ void CreateDirectoryHandler::handle(Client &client) {
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO: handle <path>
|
||||
|
||||
// check that <path> exists and is directory
|
||||
// check that <path> exists, is directory and isn't forbidden
|
||||
AbstractFSNode *node = g_system->getFilesystemFactory()->makeFileNodePath(path);
|
||||
if (!HandlerUtils::permittedPath(node->getPath())) {
|
||||
handleError(client, _("Invalid path!"));
|
||||
return;
|
||||
}
|
||||
if (!node->exists()) {
|
||||
handleError(client, _("Parent directory doesn't exists!"));
|
||||
return;
|
||||
|
||||
@@ -44,6 +44,12 @@ void DownloadFileHandler::handle(Client &client) {
|
||||
return;
|
||||
}
|
||||
|
||||
// check that <path> contains no '../'
|
||||
if (HandlerUtils::hasForbiddenCombinations(path)) {
|
||||
HandlerUtils::setFilesManagerErrorMessageHandler(client, _("Invalid path!"));
|
||||
return;
|
||||
}
|
||||
|
||||
// transform virtual path to actual file system one
|
||||
Common::String prefixToRemove = "", prefixToAdd = "";
|
||||
if (!transformPath(path, prefixToRemove, prefixToAdd, false) || path.empty()) {
|
||||
@@ -51,10 +57,12 @@ void DownloadFileHandler::handle(Client &client) {
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO: handle <path>
|
||||
|
||||
// check that <path> exists and is directory
|
||||
// check that <path> exists, is directory and isn't forbidden
|
||||
AbstractFSNode *node = g_system->getFilesystemFactory()->makeFileNodePath(path);
|
||||
if (!HandlerUtils::permittedPath(node->getPath())) {
|
||||
HandlerUtils::setFilesManagerErrorMessageHandler(client, _("Invalid path!"));
|
||||
return;
|
||||
}
|
||||
if (!node->exists()) {
|
||||
HandlerUtils::setFilesManagerErrorMessageHandler(client, _("The file doesn't exist!"));
|
||||
return;
|
||||
|
||||
@@ -52,12 +52,16 @@ bool FilesBaseHandler::transformPath(Common::String &path, Common::String &prefi
|
||||
|
||||
if (path.hasPrefix("/root")) {
|
||||
prefixToAdd = "/root/";
|
||||
prefixToRemove = "";
|
||||
prefixToRemove = (ConfMan.hasKey("rootpath", "cloud") ? ConfMan.get("rootpath", "cloud") : "");
|
||||
if (prefixToRemove.size() && prefixToRemove.lastChar() != '/' && prefixToRemove.lastChar() != '\\')
|
||||
prefixToRemove += '/';
|
||||
if (prefixToRemove == "/") prefixToRemove = "";
|
||||
path.erase(0, 5);
|
||||
if (path.size() && (path[0] == '/' || path[0] == '\\'))
|
||||
path.deleteChar(0); // if that was "/root/ab/c", it becomes "/ab/c", but we need "ab/c"
|
||||
path = prefixToRemove + path;
|
||||
if (path == "")
|
||||
path = "/"; // absolute root is '/'
|
||||
if (path != "/")
|
||||
path.deleteChar(0); // if that was "/root/ab/c", it becomes "/ab/c", but we need "ab/c"
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -81,17 +81,19 @@ bool FilesPageHandler::listDirectory(Common::String path, Common::String &conten
|
||||
return true;
|
||||
}
|
||||
|
||||
if (HandlerUtils::hasForbiddenCombinations(path))
|
||||
return false;
|
||||
|
||||
Common::String prefixToRemove = "", prefixToAdd = "";
|
||||
if (!transformPath(path, prefixToRemove, prefixToAdd))
|
||||
return false;
|
||||
|
||||
// TODO: handle <path>
|
||||
|
||||
Common::FSNode node = Common::FSNode(path);
|
||||
if (path == "/")
|
||||
node = node.getParent(); // absolute root
|
||||
|
||||
// TODO: handle <path>
|
||||
if (!HandlerUtils::permittedPath(node.getPath()))
|
||||
return false;
|
||||
|
||||
if (!node.isDirectory())
|
||||
return false;
|
||||
|
||||
@@ -46,17 +46,19 @@ Common::JSONObject ListAjaxHandler::listDirectory(Common::String path) {
|
||||
return successResult;
|
||||
}
|
||||
|
||||
if (HandlerUtils::hasForbiddenCombinations(path))
|
||||
return errorResult;
|
||||
|
||||
Common::String prefixToRemove = "", prefixToAdd = "";
|
||||
if (!transformPath(path, prefixToRemove, prefixToAdd))
|
||||
return errorResult;
|
||||
|
||||
// TODO: handle <path>
|
||||
|
||||
Common::FSNode node = Common::FSNode(path);
|
||||
if (path == "/")
|
||||
node = node.getParent(); // absolute root
|
||||
|
||||
// TODO: handle <path>
|
||||
if (!HandlerUtils::permittedPath(node.getPath()))
|
||||
return errorResult;
|
||||
|
||||
if (!node.isDirectory())
|
||||
return errorResult;
|
||||
|
||||
@@ -44,6 +44,12 @@ void UploadFileHandler::handle(Client &client) {
|
||||
return;
|
||||
}
|
||||
|
||||
// check that <path> contains no '../'
|
||||
if (HandlerUtils::hasForbiddenCombinations(path)) {
|
||||
HandlerUtils::setFilesManagerErrorMessageHandler(client, _("Invalid path!"));
|
||||
return;
|
||||
}
|
||||
|
||||
// transform virtual path to actual file system one
|
||||
Common::String prefixToRemove = "", prefixToAdd = "";
|
||||
if (!transformPath(path, prefixToRemove, prefixToAdd, false) || path.empty()) {
|
||||
@@ -51,10 +57,12 @@ void UploadFileHandler::handle(Client &client) {
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO: handle <path>
|
||||
|
||||
// check that <path> exists and is directory
|
||||
// check that <path> exists, is directory and isn't forbidden
|
||||
AbstractFSNode *node = g_system->getFilesystemFactory()->makeFileNodePath(path);
|
||||
if (!HandlerUtils::permittedPath(node->getPath())) {
|
||||
HandlerUtils::setFilesManagerErrorMessageHandler(client, _("Invalid path!"));
|
||||
return;
|
||||
}
|
||||
if (!node->exists()) {
|
||||
HandlerUtils::setFilesManagerErrorMessageHandler(client, _("The parent directory doesn't exist!"));
|
||||
return;
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
|
||||
#include "backends/networking/sdl_net/handlerutils.h"
|
||||
#include "backends/networking/sdl_net/localwebserver.h"
|
||||
#include "backends/saves/default/default-saves.h"
|
||||
#include "common/archive.h"
|
||||
#include "common/config-manager.h"
|
||||
#include "common/file.h"
|
||||
@@ -98,6 +99,75 @@ Common::String HandlerUtils::readEverythingFromStream(Common::SeekableReadStream
|
||||
return result;
|
||||
}
|
||||
|
||||
Common::String HandlerUtils::normalizePath(const Common::String &path) {
|
||||
Common::String normalized;
|
||||
bool slash = false;
|
||||
for (uint32 i = 0; i < path.size(); ++i) {
|
||||
char c = path[i];
|
||||
if (c == '\\' || c == '/') {
|
||||
slash = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (slash) {
|
||||
normalized += '/';
|
||||
slash = false;
|
||||
}
|
||||
|
||||
if ('A' <= c && c <= 'Z') {
|
||||
normalized += c - 'A' + 'a';
|
||||
} else {
|
||||
normalized += c;
|
||||
}
|
||||
}
|
||||
if (slash) normalized += '/';
|
||||
return normalized;
|
||||
}
|
||||
|
||||
bool HandlerUtils::hasForbiddenCombinations(const Common::String &path) {
|
||||
return (path.contains("../") || path.contains("..\\"));
|
||||
}
|
||||
|
||||
bool HandlerUtils::isBlacklisted(const Common::String &path) {
|
||||
const char *blacklist[] = {
|
||||
"/etc",
|
||||
"/bin",
|
||||
"c:/windows" // just saying: I know guys who install windows on another drives
|
||||
};
|
||||
|
||||
// normalize path
|
||||
Common::String normalized = normalizePath(path);
|
||||
|
||||
uint32 size = sizeof(blacklist) / sizeof(const char *);
|
||||
for (uint32 i = 0; i < size; ++i)
|
||||
if (normalized.hasPrefix(blacklist[i]))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool HandlerUtils::hasPermittedPrefix(const Common::String &path) {
|
||||
// normalize path
|
||||
Common::String normalized = normalizePath(path);
|
||||
|
||||
// prefix for /root/
|
||||
Common::String prefix;
|
||||
if (ConfMan.hasKey("rootpath", "cloud")) {
|
||||
prefix = normalizePath(ConfMan.get("rootpath", "cloud"));
|
||||
if (prefix == "/" || normalized.hasPrefix(prefix))
|
||||
return true;
|
||||
}
|
||||
|
||||
// prefix for /saves/
|
||||
DefaultSaveFileManager *manager = dynamic_cast<DefaultSaveFileManager *>(g_system->getSavefileManager());
|
||||
prefix = (manager ? manager->concatWithSavesPath("") : ConfMan.get("savepath"));
|
||||
return (normalized.hasPrefix(normalizePath(prefix)));
|
||||
}
|
||||
|
||||
bool HandlerUtils::permittedPath(const Common::String path) {
|
||||
return hasPermittedPrefix(path) && !isBlacklisted(path);
|
||||
}
|
||||
|
||||
void HandlerUtils::setMessageHandler(Client &client, Common::String message, Common::String redirectTo) {
|
||||
Common::String response = "<html><head><title>ScummVM</title></head><body>{message}</body></html>";
|
||||
|
||||
|
||||
@@ -35,6 +35,12 @@ public:
|
||||
static Common::SeekableReadStream *getArchiveFile(Common::String name);
|
||||
static Common::String readEverythingFromStream(Common::SeekableReadStream *const stream);
|
||||
|
||||
static Common::String normalizePath(const Common::String &path);
|
||||
static bool hasForbiddenCombinations(const Common::String &path);
|
||||
static bool isBlacklisted(const Common::String &path);
|
||||
static bool hasPermittedPrefix(const Common::String &path);
|
||||
static bool permittedPath(const Common::String path);
|
||||
|
||||
static void setMessageHandler(Client &client, Common::String message, Common::String redirectTo = "");
|
||||
static void setFilesManagerErrorMessageHandler(Client &client, Common::String message, Common::String redirectTo = "");
|
||||
};
|
||||
|
||||
@@ -127,13 +127,18 @@ void UploadFileClientHandler::handleBlockHeaders(Client *client) {
|
||||
if (filename.empty())
|
||||
return;
|
||||
|
||||
// TODO: handle <filename>, <path> + <filename>
|
||||
if (HandlerUtils::hasForbiddenCombinations(filename))
|
||||
return;
|
||||
|
||||
// check that <path>/<filename> doesn't exist
|
||||
Common::String path = _parentDirectoryPath;
|
||||
if (path.lastChar() != '/' && path.lastChar() != '\\')
|
||||
path += '/';
|
||||
AbstractFSNode *originalNode = g_system->getFilesystemFactory()->makeFileNodePath(path + filename);
|
||||
if (!HandlerUtils::permittedPath(originalNode->getPath())) {
|
||||
setErrorMessageHandler(*client, _("Invalid path!"));
|
||||
return;
|
||||
}
|
||||
if (originalNode->exists()) {
|
||||
setErrorMessageHandler(*client, _("There is a file with that name in the parent directory!"));
|
||||
return;
|
||||
|
||||
Reference in New Issue
Block a user