From 2ed78e82a803ced529e32dbce48b3a9435ca4432 Mon Sep 17 00:00:00 2001 From: Michael Friedrich Date: Thu, 12 Feb 2015 09:12:55 +0100 Subject: [PATCH] Validate configured TimePeriod ranges Requires re-throwing the hidden exceptions inside the existing timeperiod code and validating the configured time range strings then. fixes #7576 --- lib/icinga/icinga-type.conf | 2 ++ lib/icinga/legacytimeperiod.cpp | 34 +++++++++++++++++++++++++++----- lib/icinga/timeperiod.cpp | 35 +++++++++++++++++++++++++++++++++ lib/icinga/timeperiod.hpp | 2 ++ 4 files changed, 68 insertions(+), 5 deletions(-) diff --git a/lib/icinga/icinga-type.conf b/lib/icinga/icinga-type.conf index 18d4c9f85..175423f81 100644 --- a/lib/icinga/icinga-type.conf +++ b/lib/icinga/icinga-type.conf @@ -179,6 +179,8 @@ } %type TimePeriod %inherits CustomVarObject { + %validator "ValidateTimePeriodRanges", + %attribute %string "display_name", %require "update", diff --git a/lib/icinga/legacytimeperiod.cpp b/lib/icinga/legacytimeperiod.cpp index 6e380ad8c..d78cc00f4 100644 --- a/lib/icinga/legacytimeperiod.cpp +++ b/lib/icinga/legacytimeperiod.cpp @@ -289,7 +289,11 @@ void LegacyTimePeriod::ParseTimeRange(const String& timerange, tm *begin, tm *en String second = def.SubStr(pos + 1); second.Trim(); - ParseTimeSpec(first, begin, NULL, reference); + try { + ParseTimeSpec(first, begin, NULL, reference); + } catch (std::exception&) { + throw; + } /* If the second definition starts with a number we need * to add the first word from the first definition, e.g.: @@ -310,9 +314,17 @@ void LegacyTimePeriod::ParseTimeRange(const String& timerange, tm *begin, tm *en second = first.SubStr(0, xpos + 1) + second; } - ParseTimeSpec(second, NULL, end, reference); + try { + ParseTimeSpec(second, NULL, end, reference); + } catch (std::exception&) { + throw; + } } else { - ParseTimeSpec(def, begin, end, reference); + try { + ParseTimeSpec(def, begin, end, reference); + } catch (std::exception&) { + throw; + } } } @@ -364,7 +376,13 @@ void LegacyTimePeriod::ProcessTimeRangeRaw(const String& timerange, tm *referenc Dictionary::Ptr LegacyTimePeriod::ProcessTimeRange(const String& timestamp, tm *reference) { tm begin, end; - ProcessTimeRangeRaw(timestamp, reference, &begin, &end); + + try { + ProcessTimeRangeRaw(timestamp, reference, &begin, &end); + } catch (std::exception&) { + throw; + } + Dictionary::Ptr segment = new Dictionary(); segment->Set("begin", (long)mktime(&begin)); segment->Set("end", (long)mktime(&end)); @@ -378,7 +396,13 @@ void LegacyTimePeriod::ProcessTimeRanges(const String& timeranges, tm *reference boost::algorithm::split(ranges, timeranges, boost::is_any_of(",")); BOOST_FOREACH(const String& range, ranges) { - Dictionary::Ptr segment = ProcessTimeRange(range, reference); + Dictionary::Ptr segment; + try { + segment = ProcessTimeRange(range, reference); + } catch (std::exception&) { + throw; + } + if (segment->Get("begin") >= segment->Get("end")) BOOST_THROW_EXCEPTION(std::invalid_argument("Time period segment ends before it begins")); diff --git a/lib/icinga/timeperiod.cpp b/lib/icinga/timeperiod.cpp index 985aa6f12..e03e3a5b3 100644 --- a/lib/icinga/timeperiod.cpp +++ b/lib/icinga/timeperiod.cpp @@ -18,8 +18,10 @@ ******************************************************************************/ #include "icinga/timeperiod.hpp" +#include "icinga/legacytimeperiod.hpp" #include "base/dynamictype.hpp" #include "base/objectlock.hpp" +#include "base/exception.hpp" #include "base/logger.hpp" #include "base/timer.hpp" #include "base/utility.hpp" @@ -28,6 +30,7 @@ using namespace icinga; REGISTER_TYPE(TimePeriod); +REGISTER_SCRIPTFUNCTION(ValidateTimePeriodRanges, &TimePeriod::ValidateRanges); static Timer::Ptr l_UpdateTimer; @@ -297,3 +300,35 @@ void TimePeriod::Dump(void) Log(LogDebug, "TimePeriod", "---"); } + +void TimePeriod::ValidateRanges(const String& location, const TimePeriod::Ptr& object) +{ + Dictionary::Ptr ranges = object->GetRanges(); + + if (!ranges) + return; + + /* create a fake time environment to validate the definitions */ + time_t begin = Utility::GetTime(); + time_t end = begin + 24 * 60 * 60; + tm reference = Utility::LocalTime(end); + tm begin_tm, end_tm; + Array::Ptr segments = new Array(); + + ObjectLock olock(ranges); + BOOST_FOREACH(const Dictionary::Pair& kv, ranges) { + try { + LegacyTimePeriod::ParseTimeSpec(kv.first, &begin_tm, &end_tm, &reference); + } catch (std::exception&) { + BOOST_THROW_EXCEPTION(ScriptError("Validation failed for " + + location + ": Invalid time specification.", object->GetDebugInfo())); + } + + try { + LegacyTimePeriod::ProcessTimeRanges(kv.second, &reference, segments); + } catch (std::exception&) { + BOOST_THROW_EXCEPTION(ScriptError("Validation failed for " + + location + ": Invalid time range definition.", object->GetDebugInfo())); + } + } +} diff --git a/lib/icinga/timeperiod.hpp b/lib/icinga/timeperiod.hpp index 037ba6f62..81b3d2de8 100644 --- a/lib/icinga/timeperiod.hpp +++ b/lib/icinga/timeperiod.hpp @@ -46,6 +46,8 @@ public: bool IsInside(double ts) const; double FindNextTransition(double begin); + static void ValidateRanges(const String& location, const TimePeriod::Ptr& object); + private: void AddSegment(double s, double end); void AddSegment(const Dictionary::Ptr& segment);