From: Gunnar Beutner Date: Thu, 16 Apr 2015 06:47:26 +0000 (+0200) Subject: Implement sandbox mode for the config parser X-Git-Tag: v2.4.0~726 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=d93bcedaadf17aad457f12e5448b27c6849cdb34;p=icinga2 Implement sandbox mode for the config parser fixes #9068 --- diff --git a/lib/base/array-script.cpp b/lib/base/array-script.cpp index 6118d11b4..5ab103608 100644 --- a/lib/base/array-script.cpp +++ b/lib/base/array-script.cpp @@ -129,15 +129,15 @@ Object::Ptr Array::GetPrototype(void) if (!prototype) { prototype = new Dictionary(); - prototype->Set("len", new Function(WrapFunction(ArrayLen))); + prototype->Set("len", new Function(WrapFunction(ArrayLen), true)); prototype->Set("set", new Function(WrapFunction(ArraySet))); prototype->Set("add", new Function(WrapFunction(ArrayAdd))); prototype->Set("remove", new Function(WrapFunction(ArrayRemove))); - prototype->Set("contains", new Function(WrapFunction(ArrayContains))); + prototype->Set("contains", new Function(WrapFunction(ArrayContains), true)); prototype->Set("clear", new Function(WrapFunction(ArrayClear))); - prototype->Set("sort", new Function(WrapFunction(ArraySort))); - prototype->Set("clone", new Function(WrapFunction(ArrayClone))); - prototype->Set("join", new Function(WrapFunction(ArrayJoin))); + prototype->Set("sort", new Function(WrapFunction(ArraySort), true)); + prototype->Set("clone", new Function(WrapFunction(ArrayClone), true)); + prototype->Set("join", new Function(WrapFunction(ArrayJoin), true)); } return prototype; diff --git a/lib/base/boolean-script.cpp b/lib/base/boolean-script.cpp index 4a4ceba84..f4a1c0006 100644 --- a/lib/base/boolean-script.cpp +++ b/lib/base/boolean-script.cpp @@ -38,7 +38,7 @@ Object::Ptr Boolean::GetPrototype(void) if (!prototype) { prototype = new Dictionary(); - prototype->Set("to_string", new Function(WrapFunction(BooleanToString))); + prototype->Set("to_string", new Function(WrapFunction(BooleanToString), true)); } return prototype; diff --git a/lib/base/dictionary-script.cpp b/lib/base/dictionary-script.cpp index 3834e39fc..1eb1d53d2 100644 --- a/lib/base/dictionary-script.cpp +++ b/lib/base/dictionary-script.cpp @@ -65,11 +65,11 @@ Object::Ptr Dictionary::GetPrototype(void) if (!prototype) { prototype = new Dictionary(); - prototype->Set("len", new Function(WrapFunction(DictionaryLen))); + prototype->Set("len", new Function(WrapFunction(DictionaryLen), true)); prototype->Set("set", new Function(WrapFunction(DictionarySet))); prototype->Set("remove", new Function(WrapFunction(DictionaryRemove))); - prototype->Set("contains", new Function(WrapFunction(DictionaryContains))); - prototype->Set("clone", new Function(WrapFunction(DictionaryClone))); + prototype->Set("contains", new Function(WrapFunction(DictionaryContains), true)); + prototype->Set("clone", new Function(WrapFunction(DictionaryClone), true)); } return prototype; diff --git a/lib/base/function.cpp b/lib/base/function.cpp index f33cde8c1..176980768 100644 --- a/lib/base/function.cpp +++ b/lib/base/function.cpp @@ -25,8 +25,8 @@ using namespace icinga; REGISTER_PRIMITIVE_TYPE_NOINST(Function, Function::GetPrototype()); -Function::Function(const Callback& function) - : m_Callback(function) +Function::Function(const Callback& function, bool side_effect_free) + : m_Callback(function), m_SideEffectFree(side_effect_free) { } Value Function::Invoke(const std::vector& arguments) @@ -34,3 +34,8 @@ Value Function::Invoke(const std::vector& arguments) return m_Callback(arguments); } +bool Function::IsSideEffectFree(void) const +{ + return m_SideEffectFree; +} + diff --git a/lib/base/function.hpp b/lib/base/function.hpp index 16c5285bb..5d912ca89 100644 --- a/lib/base/function.hpp +++ b/lib/base/function.hpp @@ -42,14 +42,16 @@ public: typedef boost::function& arguments)> Callback; - Function(const Callback& function); + Function(const Callback& function, bool side_effect_free = false); Value Invoke(const std::vector& arguments = std::vector()); + bool IsSideEffectFree(void) const; static Object::Ptr GetPrototype(void); private: Callback m_Callback; + bool m_SideEffectFree; }; #define REGISTER_SCRIPTFUNCTION(name, callback) \ @@ -61,6 +63,15 @@ private: INITIALIZE_ONCE(RegisterFunction); \ } } } +#define REGISTER_SAFE_SCRIPTFUNCTION(name, callback) \ + namespace { namespace UNIQUE_NAME(sf) { namespace sf ## name { \ + void RegisterFunction(void) { \ + Function::Ptr sf = new icinga::Function(WrapFunction(callback), true); \ + ScriptGlobal::Set(#name, sf); \ + } \ + INITIALIZE_ONCE(RegisterFunction); \ + } } } + } #endif /* SCRIPTFUNCTION_H */ diff --git a/lib/base/json-script.cpp b/lib/base/json-script.cpp index 50e5fa7b5..7b7bbe01c 100644 --- a/lib/base/json-script.cpp +++ b/lib/base/json-script.cpp @@ -36,8 +36,8 @@ static void InitializeJsonObj(void) Dictionary::Ptr jsonObj = new Dictionary(); /* Methods */ - jsonObj->Set("encode", new Function(WrapFunction(JsonEncodeShim))); - jsonObj->Set("decode", new Function(WrapFunction(JsonDecode))); + jsonObj->Set("encode", new Function(WrapFunction(JsonEncodeShim), true)); + jsonObj->Set("decode", new Function(WrapFunction(JsonDecode), true)); ScriptGlobal::Set("Json", jsonObj); } diff --git a/lib/base/math-script.cpp b/lib/base/math-script.cpp index b10afb71f..bd35ec47a 100644 --- a/lib/base/math-script.cpp +++ b/lib/base/math-script.cpp @@ -174,27 +174,27 @@ static void InitializeMathObj(void) mathObj->Set("SQRT2", 1.41421356237309504880); /* Methods */ - mathObj->Set("abs", new Function(WrapFunction(MathAbs))); - mathObj->Set("acos", new Function(WrapFunction(MathAcos))); - mathObj->Set("asin", new Function(WrapFunction(MathAsin))); - mathObj->Set("atan", new Function(WrapFunction(MathAtan))); - mathObj->Set("atan2", new Function(WrapFunction(MathAtan2))); - mathObj->Set("ceil", new Function(WrapFunction(MathCeil))); - mathObj->Set("cos", new Function(WrapFunction(MathCos))); - mathObj->Set("exp", new Function(WrapFunction(MathExp))); - mathObj->Set("floor", new Function(WrapFunction(MathFloor))); - mathObj->Set("log", new Function(WrapFunction(MathLog))); - mathObj->Set("max", new Function(WrapFunction(MathMax))); - mathObj->Set("min", new Function(WrapFunction(MathMin))); - mathObj->Set("pow", new Function(WrapFunction(MathPow))); - mathObj->Set("random", new Function(WrapFunction(MathRandom))); - mathObj->Set("round", new Function(WrapFunction(MathRound))); - mathObj->Set("sin", new Function(WrapFunction(MathSin))); - mathObj->Set("sqrt", new Function(WrapFunction(MathSqrt))); - mathObj->Set("tan", new Function(WrapFunction(MathTan))); - mathObj->Set("isnan", new Function(WrapFunction(MathIsnan))); - mathObj->Set("isinf", new Function(WrapFunction(MathIsinf))); - mathObj->Set("sign", new Function(WrapFunction(MathSign))); + mathObj->Set("abs", new Function(WrapFunction(MathAbs), true)); + mathObj->Set("acos", new Function(WrapFunction(MathAcos), true)); + mathObj->Set("asin", new Function(WrapFunction(MathAsin), true)); + mathObj->Set("atan", new Function(WrapFunction(MathAtan), true)); + mathObj->Set("atan2", new Function(WrapFunction(MathAtan2), true)); + mathObj->Set("ceil", new Function(WrapFunction(MathCeil), true)); + mathObj->Set("cos", new Function(WrapFunction(MathCos), true)); + mathObj->Set("exp", new Function(WrapFunction(MathExp), true)); + mathObj->Set("floor", new Function(WrapFunction(MathFloor), true)); + mathObj->Set("log", new Function(WrapFunction(MathLog), true)); + mathObj->Set("max", new Function(WrapFunction(MathMax), true)); + mathObj->Set("min", new Function(WrapFunction(MathMin), true)); + mathObj->Set("pow", new Function(WrapFunction(MathPow), true)); + mathObj->Set("random", new Function(WrapFunction(MathRandom), true)); + mathObj->Set("round", new Function(WrapFunction(MathRound), true)); + mathObj->Set("sin", new Function(WrapFunction(MathSin), true)); + mathObj->Set("sqrt", new Function(WrapFunction(MathSqrt), true)); + mathObj->Set("tan", new Function(WrapFunction(MathTan), true)); + mathObj->Set("isnan", new Function(WrapFunction(MathIsnan), true)); + mathObj->Set("isinf", new Function(WrapFunction(MathIsinf), true)); + mathObj->Set("sign", new Function(WrapFunction(MathSign), true)); ScriptGlobal::Set("Math", mathObj); } diff --git a/lib/base/number-script.cpp b/lib/base/number-script.cpp index ab4c75cbc..f212dc32d 100644 --- a/lib/base/number-script.cpp +++ b/lib/base/number-script.cpp @@ -38,7 +38,7 @@ Object::Ptr Number::GetPrototype(void) if (!prototype) { prototype = new Dictionary(); - prototype->Set("to_string", new Function(WrapFunction(NumberToString))); + prototype->Set("to_string", new Function(WrapFunction(NumberToString), true)); } return prototype; diff --git a/lib/base/object-script.cpp b/lib/base/object-script.cpp index 0a42a6d24..f826df915 100644 --- a/lib/base/object-script.cpp +++ b/lib/base/object-script.cpp @@ -38,7 +38,7 @@ Object::Ptr Object::GetPrototype(void) if (!prototype) { prototype = new Dictionary(); - prototype->Set("to_string", new Function(WrapFunction(ObjectToString))); + prototype->Set("to_string", new Function(WrapFunction(ObjectToString), true)); } return prototype; diff --git a/lib/base/scriptframe.cpp b/lib/base/scriptframe.cpp index da03a99a0..634167e68 100644 --- a/lib/base/scriptframe.cpp +++ b/lib/base/scriptframe.cpp @@ -25,13 +25,13 @@ using namespace icinga; boost::thread_specific_ptr > ScriptFrame::m_ScriptFrames; ScriptFrame::ScriptFrame(void) - : Locals(new Dictionary()), Self(ScriptGlobal::GetGlobals()) + : Locals(new Dictionary()), Self(ScriptGlobal::GetGlobals()), Sandboxed(false) { PushFrame(this); } ScriptFrame::ScriptFrame(const Value& self) - : Locals(new Dictionary()), Self(self) + : Locals(new Dictionary()), Self(self), Sandboxed(false) { PushFrame(this); } diff --git a/lib/base/scriptframe.hpp b/lib/base/scriptframe.hpp index f4e47151a..915989e65 100644 --- a/lib/base/scriptframe.hpp +++ b/lib/base/scriptframe.hpp @@ -32,6 +32,7 @@ struct I2_BASE_API ScriptFrame { Dictionary::Ptr Locals; Value Self; + bool Sandboxed; ScriptFrame(void); ScriptFrame(const Value& self); diff --git a/lib/base/scriptutils.cpp b/lib/base/scriptutils.cpp index b6b9df8c6..5e6ff0743 100644 --- a/lib/base/scriptutils.cpp +++ b/lib/base/scriptutils.cpp @@ -33,24 +33,24 @@ using namespace icinga; -REGISTER_SCRIPTFUNCTION(regex, &ScriptUtils::Regex); -REGISTER_SCRIPTFUNCTION(match, &Utility::Match); -REGISTER_SCRIPTFUNCTION(len, &ScriptUtils::Len); -REGISTER_SCRIPTFUNCTION(union, &ScriptUtils::Union); -REGISTER_SCRIPTFUNCTION(intersection, &ScriptUtils::Intersection); +REGISTER_SAFE_SCRIPTFUNCTION(regex, &ScriptUtils::Regex); +REGISTER_SAFE_SCRIPTFUNCTION(match, &Utility::Match); +REGISTER_SAFE_SCRIPTFUNCTION(len, &ScriptUtils::Len); +REGISTER_SAFE_SCRIPTFUNCTION(union, &ScriptUtils::Union); +REGISTER_SAFE_SCRIPTFUNCTION(intersection, &ScriptUtils::Intersection); REGISTER_SCRIPTFUNCTION(log, &ScriptUtils::Log); REGISTER_SCRIPTFUNCTION(range, &ScriptUtils::Range); REGISTER_SCRIPTFUNCTION(exit, &Application::Exit); -REGISTER_SCRIPTFUNCTION(typeof, &ScriptUtils::TypeOf); -REGISTER_SCRIPTFUNCTION(keys, &ScriptUtils::Keys); -REGISTER_SCRIPTFUNCTION(random, &Utility::Random); -REGISTER_SCRIPTFUNCTION(get_object, &ScriptUtils::GetObject); -REGISTER_SCRIPTFUNCTION(get_objects, &ScriptUtils::GetObjects); +REGISTER_SAFE_SCRIPTFUNCTION(typeof, &ScriptUtils::TypeOf); +REGISTER_SAFE_SCRIPTFUNCTION(keys, &ScriptUtils::Keys); +REGISTER_SAFE_SCRIPTFUNCTION(random, &Utility::Random); +REGISTER_SAFE_SCRIPTFUNCTION(get_object, &ScriptUtils::GetObject); +REGISTER_SAFE_SCRIPTFUNCTION(get_objects, &ScriptUtils::GetObjects); REGISTER_SCRIPTFUNCTION(assert, &ScriptUtils::Assert); -REGISTER_SCRIPTFUNCTION(string, &ScriptUtils::CastString); -REGISTER_SCRIPTFUNCTION(number, &ScriptUtils::CastNumber); -REGISTER_SCRIPTFUNCTION(bool, &ScriptUtils::CastBool); -REGISTER_SCRIPTFUNCTION(get_time, &Utility::GetTime); +REGISTER_SAFE_SCRIPTFUNCTION(string, &ScriptUtils::CastString); +REGISTER_SAFE_SCRIPTFUNCTION(number, &ScriptUtils::CastNumber); +REGISTER_SAFE_SCRIPTFUNCTION(bool, &ScriptUtils::CastBool); +REGISTER_SAFE_SCRIPTFUNCTION(get_time, &Utility::GetTime); String ScriptUtils::CastString(const Value& value) { diff --git a/lib/base/string-script.cpp b/lib/base/string-script.cpp index c76ef90dc..ed5a18ba5 100644 --- a/lib/base/string-script.cpp +++ b/lib/base/string-script.cpp @@ -133,15 +133,15 @@ Object::Ptr String::GetPrototype(void) if (!prototype) { prototype = new Dictionary(); - prototype->Set("len", new Function(WrapFunction(StringLen))); - prototype->Set("to_string", new Function(WrapFunction(StringToString))); - prototype->Set("substr", new Function(WrapFunction(StringSubstr))); - prototype->Set("upper", new Function(WrapFunction(StringUpper))); - prototype->Set("lower", new Function(WrapFunction(StringLower))); - prototype->Set("split", new Function(WrapFunction(StringSplit))); - prototype->Set("find", new Function(WrapFunction(StringFind))); - prototype->Set("contains", new Function(WrapFunction(StringContains))); - prototype->Set("replace", new Function(WrapFunction(StringReplace))); + prototype->Set("len", new Function(WrapFunction(StringLen), true)); + prototype->Set("to_string", new Function(WrapFunction(StringToString), true)); + prototype->Set("substr", new Function(WrapFunction(StringSubstr), true)); + prototype->Set("upper", new Function(WrapFunction(StringUpper), true)); + prototype->Set("lower", new Function(WrapFunction(StringLower), true)); + prototype->Set("split", new Function(WrapFunction(StringSplit), true)); + prototype->Set("find", new Function(WrapFunction(StringFind), true)); + prototype->Set("contains", new Function(WrapFunction(StringContains), true)); + prototype->Set("replace", new Function(WrapFunction(StringReplace), true)); } return prototype; diff --git a/lib/cli/consolecommand.cpp b/lib/cli/consolecommand.cpp index b5845b681..79d1adca4 100644 --- a/lib/cli/consolecommand.cpp +++ b/lib/cli/consolecommand.cpp @@ -59,6 +59,7 @@ void ConsoleCommand::InitParameters(boost::program_options::options_description& { visibleDesc.add_options() ("connect,c", po::value(), "connect to an Icinga 2 instance") + ("sandbox", "enable sandbox mode") ; } @@ -194,6 +195,15 @@ int ConsoleCommand::Run(const po::variables_map& vm, const std::vectorIsSideEffectFree() && frame.Sandboxed) + BOOST_THROW_EXCEPTION(ScriptError("Function is not marked as safe for sandbox mode.", m_DebugInfo)); + std::vector arguments; BOOST_FOREACH(Expression *arg, m_Args) { ExpressionResult argres = arg->Evaluate(frame); @@ -483,6 +486,9 @@ ExpressionResult GetScopeExpression::DoEvaluate(ScriptFrame& frame, DebugHint *d ExpressionResult SetExpression::DoEvaluate(ScriptFrame& frame, DebugHint *dhint) const { + if (frame.Sandboxed) + BOOST_THROW_EXCEPTION(ScriptError("Assignments are not allowed in sandbox mode.", m_DebugInfo)); + DebugHint *psdhint = dhint; Value parent; @@ -565,6 +571,9 @@ ExpressionResult ConditionalExpression::DoEvaluate(ScriptFrame& frame, DebugHint ExpressionResult WhileExpression::DoEvaluate(ScriptFrame& frame, DebugHint *dhint) const { + if (frame.Sandboxed) + BOOST_THROW_EXCEPTION(ScriptError("While loops are not allowed in sandbox mode.", m_DebugInfo)); + for (;;) { ExpressionResult condition = m_Condition->Evaluate(frame, dhint); CHECK_RESULT(condition); @@ -618,6 +627,9 @@ bool IndexerExpression::GetReference(ScriptFrame& frame, bool init_dict, Value * if (dhint) psdhint = *dhint; + if (frame.Sandboxed) + init_dict = false; + if (m_Operand1->GetReference(frame, init_dict, &vparent, &vindex, &psdhint)) { if (init_dict && VMOps::GetField(vparent, vindex, m_Operand1->GetDebugInfo()).IsEmpty()) VMOps::SetField(vparent, vindex, new Dictionary(), m_Operand1->GetDebugInfo()); @@ -687,6 +699,9 @@ void icinga::BindToScope(Expression *& expr, ScopeSpecifier scopeSpec) ExpressionResult ImportExpression::DoEvaluate(ScriptFrame& frame, DebugHint *dhint) const { + if (frame.Sandboxed) + BOOST_THROW_EXCEPTION(ScriptError("Imports are not allowed in sandbox mode.", m_DebugInfo)); + String type = VMOps::GetField(frame.Self, "type", m_DebugInfo); ExpressionResult nameres = m_Name->Evaluate(frame); CHECK_RESULT(nameres); @@ -713,6 +728,9 @@ ExpressionResult FunctionExpression::DoEvaluate(ScriptFrame& frame, DebugHint *d ExpressionResult ApplyExpression::DoEvaluate(ScriptFrame& frame, DebugHint *dhint) const { + if (frame.Sandboxed) + BOOST_THROW_EXCEPTION(ScriptError("Apply rules are not allowed in sandbox mode.", m_DebugInfo)); + ExpressionResult nameres = m_Name->Evaluate(frame); CHECK_RESULT(nameres); @@ -722,6 +740,9 @@ ExpressionResult ApplyExpression::DoEvaluate(ScriptFrame& frame, DebugHint *dhin ExpressionResult ObjectExpression::DoEvaluate(ScriptFrame& frame, DebugHint *dhint) const { + if (frame.Sandboxed) + BOOST_THROW_EXCEPTION(ScriptError("Object definitions are not allowed in sandbox mode.", m_DebugInfo)); + String name; if (m_Name) { @@ -737,6 +758,9 @@ ExpressionResult ObjectExpression::DoEvaluate(ScriptFrame& frame, DebugHint *dhi ExpressionResult ForExpression::DoEvaluate(ScriptFrame& frame, DebugHint *dhint) const { + if (frame.Sandboxed) + BOOST_THROW_EXCEPTION(ScriptError("For loops are not allowed in sandbox mode.", m_DebugInfo)); + ExpressionResult valueres = m_Value->Evaluate(frame, dhint); CHECK_RESULT(valueres);