Implement sandbox mode for the config parser

fixes #9068
This commit is contained in:
Gunnar Beutner 2015-04-16 08:47:26 +02:00
parent 9a3326fe66
commit d93bcedaad
15 changed files with 113 additions and 62 deletions

View 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;

View 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;

View 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;

View 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;
}

View 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 */

View 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);
}

View 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);
}

View 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;

View 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;

View 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);
}

View File

@ -32,6 +32,7 @@ struct I2_BASE_API ScriptFrame
{
Dictionary::Ptr Locals;
Value Self;
bool Sandboxed;
ScriptFrame(void);
ScriptFrame(const Value& self);

View File

@ -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)
{

View 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;

View 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()) {

View 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);