From: Francis Dupont Date: Mon, 19 May 2025 22:20:07 +0000 (+0200) Subject: [#3831] Backported X-Git-Tag: Kea-2.6.3~8 X-Git-Url: http://git.ipfire.org/gitweb/gitweb.cgi?a=commitdiff_plain;h=dcd23cc5ef5af8403b755b22c8a07e0b901e7869;p=thirdparty%2Fkea.git [#3831] Backported --- diff --git a/src/lib/util/filesystem.cc b/src/lib/util/filesystem.cc index cf7a506f2a..ecfe29d3e6 100644 --- a/src/lib/util/filesystem.cc +++ b/src/lib/util/filesystem.cc @@ -14,7 +14,6 @@ #include #include #include -#include #include #include @@ -289,6 +288,31 @@ PathChecker::validatePath(const std::string input_path_str, return (valid_path); } +std::string +PathChecker::validateDirectory(const std::string input_path_str, + bool enforce_path /* = true */) const { + std::string input_copy = trim(input_path_str); + if (!enforce_path) { + return(input_copy); + } + + // We only allow absolute path equal to default. Catch an invalid path. + if (!input_path_str.empty()) { + std::string input_copy = input_path_str; + while (!input_copy.empty() && input_copy.back() == '/') { + input_copy.pop_back(); + } + + if (input_copy != path_) { + isc_throw(BadValue, "invalid path specified: '" + << input_path_str << "', supported path is '" + << path_ << "'"); + } + } + + return (path_); +} + bool PathChecker::pathHasPermissions(mode_t permissions) { return(hasPermissions(path_, permissions)); diff --git a/src/lib/util/filesystem.h b/src/lib/util/filesystem.h index 45be0c76a0..40862d4b78 100644 --- a/src/lib/util/filesystem.h +++ b/src/lib/util/filesystem.h @@ -70,7 +70,7 @@ isFile(const std::string& path); void setUmask(); -/// \brief Paths on a filesystem +/// @brief Paths on a filesystem struct Path { /// @brief Constructor /// @@ -217,6 +217,26 @@ public: std::string validatePath(const std::string input_path_str, bool enforce_path = true) const; + /// @brief Validates a directory against a supported path. + /// + /// Used to validate a string that represents a directory that may or + /// may not end with a "/" (i.e "/foo/bar", bar is assumed to be the + /// a directory, not a file. + /// + /// If the input is empty or it matches the supported path, it returns + /// the supported path. Otherwise it throws an error. + /// + /// @param input_path_str file path to validate. + /// @param enforce_path enables validation against the supported path. If + /// it simply returns the supported path. + /// + /// @return validated path + /// + /// @throw BadValue if the input directory does not match the supported + /// path. + std::string validateDirectory(const std::string input_path_str, + bool enforce_path = true) const; + /// @brief Tests that the supported path has the given permissions. /// /// @param permissions mode_t mask of required permissions. diff --git a/src/lib/util/tests/filesystem_unittests.cc b/src/lib/util/tests/filesystem_unittests.cc index 7da8d3cfd5..22de01e5e3 100644 --- a/src/lib/util/tests/filesystem_unittests.cc +++ b/src/lib/util/tests/filesystem_unittests.cc @@ -13,6 +13,7 @@ #include #include #include +#include #include @@ -434,4 +435,139 @@ TEST_F(FileUtilTest, hasPermissions) { EXPECT_FALSE(hasPermissions(path, current_permissions)); } +// Verifies PathChecker::validateDirectory() when enforce_path is true. +TEST(PathChecker, validateDirectoryEnforcePath) { + std::string def_path(TEST_DATA_BUILDDIR); + struct Scenario { + int line_; + std::string lib_path_; + std::string exp_path_; + std::string exp_error_; + }; + + std::list scenarios = { + { + // Invalid path with slash. + __LINE__, + "/var/lib/bs/", + "", + string("invalid path specified: '/var/lib/bs/', supported path is '" + def_path + "'") + }, + { + // Invalid path without slash. + __LINE__, + "/var/lib/bs", + "", + string("invalid path specified: '/var/lib/bs', supported path is '" + def_path + "'") + }, + { + // File name is just another bad path. + __LINE__, + "mylib.so", + "", + string("invalid path specified: 'mylib.so', supported path is '" + def_path + "'") + }, + { + // Valid full path with slash. + __LINE__, + def_path + "/", + def_path, + "" + }, + { + // Valid full path without slash. + __LINE__, + def_path, + def_path, + "" + }, + { + // Invalid relative path. + __LINE__, + "../kea/", + "", + string("invalid path specified: '../kea/', supported path is '" + + def_path + "'") + } + }; + + // Create a PathChecker with a supported path of def_path. + PathChecker checker(def_path); + for (auto scenario : scenarios) { + std::ostringstream oss; + oss << " Scenario at line: " << scenario.line_; + SCOPED_TRACE(oss.str()); + std::string validated_path; + if (scenario.exp_error_.empty()) { + ASSERT_NO_THROW_LOG(validated_path = + checker.validateDirectory(scenario.lib_path_)); + EXPECT_EQ(validated_path, scenario.exp_path_); + } else { + ASSERT_THROW_MSG(validated_path = + checker.validateDirectory(scenario.lib_path_), + BadValue, scenario.exp_error_); + } + } +} + +// Verifies PathChecker::validateDirectory() when enforce_path is false. +TEST(PathChecker, validateDirectoryEnforcePathFalse) { + std::string def_path(TEST_DATA_BUILDDIR); + struct Scenario { + int line_; + std::string lib_path_; + std::string exp_path_; + std::string exp_error_; + }; + + std::list scenarios = { + { + // Invalid parent path but shouldn't care. + __LINE__, + "/var/lib/bs/", + "/var/lib/bs/", + "" + }, + { + // File name only is valid. + __LINE__, + "mylib.so", + "mylib.so", + "" + }, + { + // Valid full path. + __LINE__, + def_path + "/", + def_path + "/", + "" + }, + { + // White space for file name. + __LINE__, + " ", + "", + "" + } + }; + + // Create a PathChecker with a supported path of def_path. + PathChecker checker(def_path); + for (auto scenario : scenarios) { + std::ostringstream oss; + oss << " Scenario at line: " << scenario.line_; + SCOPED_TRACE(oss.str()); + std::string validated_path; + if (scenario.exp_error_.empty()) { + ASSERT_NO_THROW_LOG(validated_path = + checker.validateDirectory(scenario.lib_path_, false)); + EXPECT_EQ(validated_path, scenario.exp_path_); + } else { + ASSERT_THROW_MSG(validated_path = + checker.validateDirectory(scenario.lib_path_, false), + BadValue, scenario.exp_error_); + } + } +} + } // namespace