aboutsummaryrefslogtreecommitdiff
path: root/backends
diff options
context:
space:
mode:
Diffstat (limited to 'backends')
-rw-r--r--backends/networking/sdl_net/handlers/createdirectoryhandler.cpp14
-rw-r--r--backends/networking/sdl_net/handlers/downloadfilehandler.cpp14
-rw-r--r--backends/networking/sdl_net/handlers/filesbasehandler.cpp10
-rw-r--r--backends/networking/sdl_net/handlers/filespagehandler.cpp8
-rw-r--r--backends/networking/sdl_net/handlers/listajaxhandler.cpp8
-rw-r--r--backends/networking/sdl_net/handlers/uploadfilehandler.cpp14
-rw-r--r--backends/networking/sdl_net/handlerutils.cpp70
-rw-r--r--backends/networking/sdl_net/handlerutils.h6
-rw-r--r--backends/networking/sdl_net/uploadfileclienthandler.cpp7
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;