From c1ac548a778bcb2efe3e37fc56ea352fff54ec64 Mon Sep 17 00:00:00 2001 From: Gunnar Beutner Date: Wed, 10 Dec 2014 13:20:16 +0100 Subject: [PATCH] Escape special characters in repository file names fixes #7618 --- lib/base/utility.cpp | 59 +++++++++++++++++++++++++++++++++++ lib/base/utility.hpp | 3 ++ lib/cli/repositoryutility.cpp | 26 ++++++++++----- lib/cli/repositoryutility.hpp | 3 ++ 4 files changed, 83 insertions(+), 8 deletions(-) diff --git a/lib/base/utility.cpp b/lib/base/utility.cpp index 0745e1265..31c766b92 100644 --- a/lib/base/utility.cpp +++ b/lib/base/utility.cpp @@ -26,6 +26,7 @@ #include "base/utility.hpp" #include "base/json.hpp" #include "base/objectlock.hpp" +#include "base/scriptfunction.hpp" #include #include #include @@ -57,6 +58,9 @@ using namespace icinga; boost::thread_specific_ptr Utility::m_ThreadName; boost::thread_specific_ptr Utility::m_RandSeed; +REGISTER_SCRIPTFUNCTION(escape, &Utility::EscapeString); +REGISTER_SCRIPTFUNCTION(unescape, &Utility::UnescapeString); + /** * Demangles a symbol name. * @@ -1201,3 +1205,58 @@ void Utility::SaveJsonFile(const String& path, const Value& value) << boost::errinfo_file_name(tempPath)); } } + +static void HexEncode(char ch, std::ostream& os) +{ + const char *hex_chars = "0123456789ABCDEF"; + + os << hex_chars[ch >> 4 & 0x0f]; + os << hex_chars[ch & 0x0f]; +} + +static int HexDecode(char hc) +{ + if (hc >= '0' && hc <= '9') + return hc - '0'; + else if (hc >= 'a' && hc <= 'f') + return hc - 'a' + 10; + else if (hc >= 'A' && hc <= 'F') + return hc - 'A' + 10; + else + BOOST_THROW_EXCEPTION(std::invalid_argument("Invalid hex character.")); +} + +String Utility::EscapeString(const String& s, const String& chars) +{ + std::ostringstream result; + + BOOST_FOREACH(char ch, s) { + if (chars.FindFirstOf(ch) != String::NPos || ch == '%') { + result << '%'; + HexEncode(ch, result); + } else + result << ch; + } + + return result.str(); +} + +String Utility::UnescapeString(const String& s) +{ + std::ostringstream result; + + for (String::SizeType i = 0; i < s.GetLength(); i++) { + if (s[i] == '%') { + if (i + 2 > s.GetLength() - 1) + BOOST_THROW_EXCEPTION(std::invalid_argument("Invalid escape sequence.")); + + char ch = HexDecode(s[i + 1]) * 16 + HexDecode(s[i + 2]); + result << ch; + + i += 2; + } else + result << s[i]; + } + + return result.str(); +} diff --git a/lib/base/utility.hpp b/lib/base/utility.hpp index 680594a77..8300d0353 100644 --- a/lib/base/utility.hpp +++ b/lib/base/utility.hpp @@ -115,6 +115,9 @@ public: static String EscapeShellCmd(const String& s); static String EscapeShellArg(const String& s); + static String EscapeString(const String& s, const String& chars); + static String UnescapeString(const String& s); + static void SetThreadName(const String& name, bool os = true); static String GetThreadName(void); diff --git a/lib/cli/repositoryutility.cpp b/lib/cli/repositoryutility.cpp index b9d87c290..7c57984d9 100644 --- a/lib/cli/repositoryutility.cpp +++ b/lib/cli/repositoryutility.cpp @@ -86,7 +86,7 @@ String RepositoryUtility::GetRepositoryObjectConfigPath(const String& type, cons if (type == "Host") path += "hosts"; else if (type == "Service") - path += "hosts/" + object->Get("host_name"); + path += "hosts/" + EscapeName(object->Get("host_name")); else if (type == "Zone") path += "zones"; else if (type == "Endpoint") @@ -116,7 +116,7 @@ String RepositoryUtility::GetRepositoryObjectConfigFilePath(const String& type, { String path = GetRepositoryObjectConfigPath(type, object); - path += "/" + object->Get("name") + ".conf"; + path += "/" + EscapeName(object->Get("name")) + ".conf"; return path; } @@ -154,6 +154,7 @@ void RepositoryUtility::PrintObjects(std::ostream& fp, const String& type) String file = Utility::BaseName(object); boost::algorithm::replace_all(file, ".conf", ""); + file = UnescapeName(file); fp << ConsoleColorTag(Console_ForegroundMagenta | Console_Bold) << type << ConsoleColorTag(Console_Normal) << " '" << ConsoleColorTag(Console_ForegroundBlue | Console_Bold) << file << ConsoleColorTag(Console_Normal) << "'"; @@ -164,7 +165,7 @@ void RepositoryUtility::PrintObjects(std::ostream& fp, const String& type) std::vector tokens; boost::algorithm::split(tokens, prefix, boost::is_any_of("/")); - String host_name = tokens[tokens.size()-1]; + String host_name = UnescapeName(tokens[tokens.size()-1]); fp << " (on " << ConsoleColorTag(Console_ForegroundMagenta | Console_Bold) << "Host" << ConsoleColorTag(Console_Normal) << " '" << ConsoleColorTag(Console_ForegroundBlue | Console_Bold) << host_name << ConsoleColorTag(Console_Normal) << "')"; @@ -205,9 +206,9 @@ bool RepositoryUtility::AddObject(const String& name, const String& type, const String pattern; if (type == "Service") - pattern = attrs->Get("host_name") + "/" + name + ".conf"; + pattern = EscapeName(attrs->Get("host_name")) + "/" + EscapeName(name) + ".conf"; else - pattern = name + ".conf"; + pattern = EscapeName(name) + ".conf"; BOOST_FOREACH(const String& object_path, object_paths) { if (object_path.Contains(pattern)) { @@ -421,14 +422,14 @@ Dictionary::Ptr RepositoryUtility::GetObjectFromRepositoryChangeLog(const String /* internal implementation when changes are committed */ bool RepositoryUtility::AddObjectInternal(const String& name, const String& type, const Dictionary::Ptr& attrs) { - String path = GetRepositoryObjectConfigPath(type, attrs) + "/" + name + ".conf"; + String path = GetRepositoryObjectConfigPath(type, attrs) + "/" + EscapeName(name) + ".conf"; return WriteObjectToRepository(path, name, type, attrs); } bool RepositoryUtility::RemoveObjectInternal(const String& name, const String& type, const Dictionary::Ptr& attrs) { - String path = GetRepositoryObjectConfigPath(type, attrs) + "/" + name + ".conf"; + String path = GetRepositoryObjectConfigPath(type, attrs) + "/" + EscapeName(name) + ".conf"; if (!Utility::PathExists(path)) { Log(LogWarning, "cli") @@ -492,7 +493,7 @@ bool RepositoryUtility::RemoveObjectFileInternal(const String& path) bool RepositoryUtility::SetObjectAttributeInternal(const String& name, const String& type, const String& key, const Value& val, const Dictionary::Ptr& attrs) { //TODO - String path = GetRepositoryObjectConfigPath(type, attrs) + "/" + name + ".conf"; + String path = GetRepositoryObjectConfigPath(type, attrs) + "/" + EscapeName(name) + ".conf"; Dictionary::Ptr obj = GetObjectFromRepository(path); //TODO @@ -550,6 +551,15 @@ Dictionary::Ptr RepositoryUtility::GetObjectFromRepository(const String& filenam return Dictionary::Ptr(); } +String RepositoryUtility::EscapeName(const String& name) +{ + return Utility::EscapeString(name, "<>:\"/\\|?*"); +} + +String RepositoryUtility::UnescapeName(const String& name) +{ + return Utility::UnescapeString(name); +} /* * collect functions diff --git a/lib/cli/repositoryutility.hpp b/lib/cli/repositoryutility.hpp index f4a6e302a..0ce78abd4 100644 --- a/lib/cli/repositoryutility.hpp +++ b/lib/cli/repositoryutility.hpp @@ -95,6 +95,9 @@ private: static void SerializeObject(std::ostream& fp, const String& name, const String& type, const Dictionary::Ptr& object); static void FormatValue(std::ostream& fp, const Value& val); static void FormatArray(std::ostream& fp, const Array::Ptr& arr); + + static String EscapeName(const String& name); + static String UnescapeName(const String& name); }; }