diff options
Diffstat (limited to 'backends')
9 files changed, 132 insertions, 19 deletions
diff --git a/backends/networking/sdl_net/handlers/createdirectoryhandler.cpp b/backends/networking/sdl_net/handlers/createdirectoryhandler.cpp index c539525820..284bf16651 100644 --- a/backends/networking/sdl_net/handlers/createdirectoryhandler.cpp +++ b/backends/networking/sdl_net/handlers/createdirectoryhandler.cpp @@ -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; diff --git a/backends/networking/sdl_net/handlers/downloadfilehandler.cpp b/backends/networking/sdl_net/handlers/downloadfilehandler.cpp index 8f5633c6be..9e212b1a6c 100644 --- a/backends/networking/sdl_net/handlers/downloadfilehandler.cpp +++ b/backends/networking/sdl_net/handlers/downloadfilehandler.cpp @@ -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; diff --git a/backends/networking/sdl_net/handlers/filesbasehandler.cpp b/backends/networking/sdl_net/handlers/filesbasehandler.cpp index 68d7919d4a..5e5787b624 100644 --- a/backends/networking/sdl_net/handlers/filesbasehandler.cpp +++ b/backends/networking/sdl_net/handlers/filesbasehandler.cpp @@ -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; } diff --git a/backends/networking/sdl_net/handlers/filespagehandler.cpp b/backends/networking/sdl_net/handlers/filespagehandler.cpp index 56b0551ed7..0bc9237805 100644 --- a/backends/networking/sdl_net/handlers/filespagehandler.cpp +++ b/backends/networking/sdl_net/handlers/filespagehandler.cpp @@ -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; diff --git a/backends/networking/sdl_net/handlers/listajaxhandler.cpp b/backends/networking/sdl_net/handlers/listajaxhandler.cpp index ad8907cf27..57fa04d10d 100644 --- a/backends/networking/sdl_net/handlers/listajaxhandler.cpp +++ b/backends/networking/sdl_net/handlers/listajaxhandler.cpp @@ -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; diff --git a/backends/networking/sdl_net/handlers/uploadfilehandler.cpp b/backends/networking/sdl_net/handlers/uploadfilehandler.cpp index 22896a25f0..a0e992c25e 100644 --- a/backends/networking/sdl_net/handlers/uploadfilehandler.cpp +++ b/backends/networking/sdl_net/handlers/uploadfilehandler.cpp @@ -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; diff --git a/backends/networking/sdl_net/handlerutils.cpp b/backends/networking/sdl_net/handlerutils.cpp index edb1484c16..f0c62d330a 100644 --- a/backends/networking/sdl_net/handlerutils.cpp +++ b/backends/networking/sdl_net/handlerutils.cpp @@ -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>"; diff --git a/backends/networking/sdl_net/handlerutils.h b/backends/networking/sdl_net/handlerutils.h index c6722688e8..4c2eff49b6 100644 --- a/backends/networking/sdl_net/handlerutils.h +++ b/backends/networking/sdl_net/handlerutils.h @@ -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 = ""); }; diff --git a/backends/networking/sdl_net/uploadfileclienthandler.cpp b/backends/networking/sdl_net/uploadfileclienthandler.cpp index cbc1b7fb04..bceeedcc3f 100644 --- a/backends/networking/sdl_net/uploadfileclienthandler.cpp +++ b/backends/networking/sdl_net/uploadfileclienthandler.cpp @@ -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; |