From 33a1f096ac962a8a1d51e45c87206237758079c6 Mon Sep 17 00:00:00 2001 From: Wirlaburla Date: Tue, 31 Dec 2024 22:15:53 -0600 Subject: [PATCH] Rewrote resolvePath so that inaccessible paths return a failure instead. --- src/filer.h | 2 +- src/plugins/filer_local/filer_local.cpp | 83 ++++++++++++++++++------- 2 files changed, 60 insertions(+), 25 deletions(-) diff --git a/src/filer.h b/src/filer.h index 9007df2..0e2b3b6 100644 --- a/src/filer.h +++ b/src/filer.h @@ -42,7 +42,7 @@ public: virtual file_data setCWD(std::string _cwd) = 0; virtual std::filesystem::path getRoot() = 0; virtual std::filesystem::path getCWD() = 0; - virtual std::filesystem::path resolvePath(const std::string& path) = 0; + virtual bool resolvePath(const std::string& path, std::filesystem::path* target) = 0; virtual void setTransferMode(char type) { this->transfer_mode = type; } diff --git a/src/plugins/filer_local/filer_local.cpp b/src/plugins/filer_local/filer_local.cpp index 900eac0..2d17d19 100644 --- a/src/plugins/filer_local/filer_local.cpp +++ b/src/plugins/filer_local/filer_local.cpp @@ -73,46 +73,48 @@ public: return this->cwd; } - fs::path resolvePath(const std::string& path) { + bool resolvePath(const std::string& path, fs::path* target) { try { if (path.empty() || path == ".") { - return root / cwd.relative_path(); + *target = root / cwd.relative_path(); + return true; } - fs::path target_path; if (path[0] == '/') { // Absolute path relative to FTP root - target_path = root / path.substr(1); + *target = root / path.substr(1); } else { // Relative to current directory - target_path = root / cwd.relative_path() / path; + *target = root / cwd.relative_path() / path; } // Remove .. and . components - target_path = fs::weakly_canonical(target_path); + *target = fs::weakly_canonical(*target); // Make sure we haven't escaped the root - if (!target_path.string().starts_with(root.string())) { - return root / cwd.relative_path(); - } + if (!(*target).string().starts_with(root.string())) + return false; - return target_path; + return true; } catch (const std::exception& e) { logger->print(LOGLEVEL_ERROR, "Path resolution error: %s", e.what()); - return root / cwd.relative_path(); + return false; } } file_data traverse(std::string dir) { struct file_data fd; try { - // Handle special cases - if (dir.empty() || dir == ".") { - fd.path = resolvePath(".").string().c_str(); + fs::path requested_path; + if (!resolvePath(dir, &requested_path)) { + fd.error = file_error{FilerStatusCodes::NoPermission, "Invalid Permissions"}; + return fd; + } + + if (dir.empty() || dir == ".") { + fd.path = requested_path.c_str(); return fd; } - - fs::path requested_path = resolvePath(dir); // Verify the requested path exists and is within root if (!fs::exists(requested_path)) { @@ -146,7 +148,12 @@ public: file_data createDirectory(std::string dir) { struct file_data fd; try { - fs::path resolved = resolvePath(dir); + fs::path resolved; + if (!resolvePath(dir, &resolved)) { + fd.error = file_error{FilerStatusCodes::NoPermission, "Invalid Permissions"}; + return fd; + } + fd.path = resolved.string().c_str(); if (fs::exists(resolved)) { @@ -165,7 +172,11 @@ public: file_data fileSize(std::string name) { struct file_data fd; try { - fs::path resolved = resolvePath(name); + fs::path resolved; + if (!resolvePath(name, &resolved)) { + fd.error = file_error{FilerStatusCodes::NoPermission, "Invalid Permissions"}; + return fd; + } fd.path = resolved.string().c_str(); if (!fs::exists(resolved)) { @@ -194,7 +205,11 @@ public: file_data deleteFile(std::string name) { struct file_data fd; try { - fs::path resolved = resolvePath(name); + fs::path resolved; + if (!resolvePath(name, &resolved)) { + fd.error = file_error{FilerStatusCodes::NoPermission, "Invalid Permissions"}; + return fd; + } fd.path = resolved.string().c_str(); if (!fs::exists(resolved)) { @@ -220,7 +235,11 @@ public: file_data readFile(std::string name) { struct file_data fd; try { - fs::path resolved = resolvePath(name); + fs::path resolved; + if (!resolvePath(name, &resolved)) { + fd.error = file_error{FilerStatusCodes::NoPermission, "Invalid Permissions"}; + return fd; + } fd.path = resolved.string().c_str(); if (!fs::exists(resolved)) { @@ -253,7 +272,11 @@ public: file_data writeFile(std::string name, unsigned char* data, int size, bool append = false) { struct file_data fd; try { - fs::path resolved = resolvePath(name); + fs::path resolved; + if (!resolvePath(name, &resolved)) { + fd.error = file_error{FilerStatusCodes::NoPermission, "Invalid Permissions"}; + return fd; + } fd.path = resolved.string().c_str(); std::ios_base::openmode omode = std::ios::out|std::ios::binary; @@ -276,8 +299,16 @@ public: struct file_data renameFile(const std::string& from, const std::string& to) { struct file_data fd; - std::filesystem::path src_path = resolvePath(from); - std::filesystem::path dst_path = resolvePath(to); + fs::path src_path; + if (!resolvePath(from, &src_path)) { + fd.error = file_error{FilerStatusCodes::NoPermission, "Invalid Source Permissions"}; + return fd; + } + fs::path dst_path; + if (!resolvePath(to, &dst_path)) { + fd.error = file_error{FilerStatusCodes::NoPermission, "Invalid Destination Permissions"}; + return fd; + } try { // Check if destination already exists @@ -300,7 +331,11 @@ public: file_data list(std::string path = ".") { struct file_data fd; try { - fs::path resolved = resolvePath(path); + fs::path resolved; + if (!resolvePath(path, &resolved)) { + fd.error = file_error{FilerStatusCodes::NoPermission, "Invalid Permissions"}; + return fd; + } fd.path = resolved.string().c_str(); if (!fs::exists(resolved)) {