]> granicus.if.org Git - icinga2/commitdiff
Implement sandbox mode for the config parser
authorGunnar Beutner <gunnar@beutner.name>
Thu, 16 Apr 2015 06:47:26 +0000 (08:47 +0200)
committerGunnar Beutner <gunnar@beutner.name>
Thu, 16 Apr 2015 06:48:17 +0000 (08:48 +0200)
fixes #9068

15 files changed:
lib/base/array-script.cpp
lib/base/boolean-script.cpp
lib/base/dictionary-script.cpp
lib/base/function.cpp
lib/base/function.hpp
lib/base/json-script.cpp
lib/base/math-script.cpp
lib/base/number-script.cpp
lib/base/object-script.cpp
lib/base/scriptframe.cpp
lib/base/scriptframe.hpp
lib/base/scriptutils.cpp
lib/base/string-script.cpp
lib/cli/consolecommand.cpp
lib/config/expression.cpp

index 6118d11b41b0b9aef8419d7a08fdd0c8203cfaa0..5ab10360866a0beacaeb888ddf6076331ffb6957 100644 (file)
@@ -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;
index 4a4ceba8482b70b0393d0638562901df3b4de87e..f4a1c000648c3d5bc57dd139128ee45da52a9228 100644 (file)
@@ -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;
index 3834e39fcc731d9f812cb829fa029ac157f2e791..1eb1d53d20edb25341d2e8c8b95a186f4b1a47de 100644 (file)
@@ -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;
index f33cde8c1b311c460c35b966a42d80ee5400ff4f..17698076862adf7c00fc8d2b9615d40494a8d385 100644 (file)
@@ -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<Value>& arguments)
@@ -34,3 +34,8 @@ Value Function::Invoke(const std::vector<Value>& arguments)
        return m_Callback(arguments);
 }
 
+bool Function::IsSideEffectFree(void) const
+{
+       return m_SideEffectFree;
+}
+
index 16c5285bbfa8831881a96edbc7e10a177e868bdb..5d912ca89c4b71566b87fa90d3983b4df9a29e3c 100644 (file)
@@ -42,14 +42,16 @@ public:
 
        typedef boost::function<Value (const std::vector<Value>& arguments)> Callback;
 
-       Function(const Callback& function);
+       Function(const Callback& function, bool side_effect_free = false);
 
        Value Invoke(const std::vector<Value>& arguments = std::vector<Value>());
+       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 */
index 50e5fa7b5ee5a88acbc2506cbfe8a2f294d3b0c3..7b7bbe01c6abcafb8945e582b9c90c9f381ceb07 100644 (file)
@@ -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);
 }
index b10afb71f949900b75b15c76d7aaaecbd55438fd..bd35ec47a6070e3e9175c9810e588d47800dc57e 100644 (file)
@@ -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);
 }
index ab4c75cbcf74c56951179aa90a1c83545d391e97..f212dc32dbc53fbe69e78c75685230738386142a 100644 (file)
@@ -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;
index 0a42a6d24ce922fc71cd40e9becfe70d12e55eb7..f826df9155ec46ff1192469204ada00a54abcb09 100644 (file)
@@ -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;
index da03a99a0f1963ce3a6da203385d367f37cafb7c..634167e687d4577a65c5493ca29d6310284276ff 100644 (file)
@@ -25,13 +25,13 @@ using namespace icinga;
 boost::thread_specific_ptr<std::stack<ScriptFrame *> > 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);
 }
index f4e47151acfb5d6018b57feea4ffeb0bfc8315ee..915989e6510a97e70fe4312fc52b80a9f1a4d95e 100644 (file)
@@ -32,6 +32,7 @@ struct I2_BASE_API ScriptFrame
 {
        Dictionary::Ptr Locals;
        Value Self;
+       bool Sandboxed;
 
        ScriptFrame(void);
        ScriptFrame(const Value& self);
index b6b9df8c6827da9c87aa531d5cbd0f536675154b..5e6ff0743a6b7f5db36509560f1884d9e9d51c0a 100644 (file)
 
 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)
 {
index c76ef90dc1ee8470b6234ece05a56aa96bde050a..ed5a18ba56e439afd6e44182fdf9a972184d5885 100644 (file)
@@ -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;
index b5845b681320903db6e1b3c7de07e0fab1a36088..79d1adca4665f0e92daeae025f0efffc01bcc433 100644 (file)
@@ -59,6 +59,7 @@ void ConsoleCommand::InitParameters(boost::program_options::options_description&
 {
        visibleDesc.add_options()
                ("connect,c", po::value<std::string>(), "connect to an Icinga 2 instance")
+               ("sandbox", "enable sandbox mode")
        ;
 }
 
@@ -194,6 +195,15 @@ int ConsoleCommand::Run(const po::variables_map& vm, const std::vector<std::stri
                session = Utility::NewUniqueID();
        }
 
+       if (vm.count("sandbox")) {
+               if (vm.count("connect")) {
+                       Log(LogCritical, "ConsoleCommand", "Sandbox mode cannot be used together with --connect.");
+                       return EXIT_FAILURE;
+               }
+
+               l_ScriptFrame.Sandboxed = true;
+       }
+
        std::cout << "Icinga (version: " << Application::GetVersion() << ")\n";
 
        while (std::cin.good()) {
index f690c3b5652da8d9261e0c00401dde26ebb1303f..03b2ceb61ded94db0cc4e591e54baf3e105e31e9 100644 (file)
@@ -414,6 +414,9 @@ ExpressionResult FunctionCallExpression::DoEvaluate(ScriptFrame& frame, DebugHin
 
        Function::Ptr func = vfunc;
 
+       if (!func->IsSideEffectFree() && frame.Sandboxed)
+               BOOST_THROW_EXCEPTION(ScriptError("Function is not marked as safe for sandbox mode.", m_DebugInfo));
+
        std::vector<Value> 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);