Added Lua sandboxing for profile-loaded Lua data. If the profile data can't be loaded...
authorMarc Cannon <vyhdycokio@gmail.com>
Wed, 2 Jan 2013 05:38:30 +0000 (05:38 +0000)
committerMarc Cannon <vyhdycokio@gmail.com>
Wed, 2 Jan 2013 05:38:30 +0000 (05:38 +0000)
src/LuaManager.cpp
src/LuaManager.h
src/LuaReference.cpp
src/LuaReference.h
src/Profile.cpp

index aea7195..bdae98a 100644 (file)
@@ -271,8 +271,14 @@ bool LuaHelpers::RunScriptFile( const CString &sFile )
        return true;
 }
 
-bool LuaHelpers::RunScriptOnStack( Lua *L, CString &sError, int iArgs, int iReturnValues )
+bool LuaHelpers::RunScriptOnStack( Lua *L, CString &sError, int iArgs, int iReturnValues, bool bSandbox )
 {
+       if( bSandbox )
+       {
+               lua_newtable( L );
+               lua_setfenv( L, -2 );
+       }
+
        int ret = lua_pcall( L, iArgs, iReturnValues, 0 );
        if( ret )
        {
@@ -285,7 +291,7 @@ bool LuaHelpers::RunScriptOnStack( Lua *L, CString &sError, int iArgs, int iRetu
        return true;
 }
 
-bool LuaHelpers::RunScript( Lua *L, const CString &sScript, const CString &sName, CString &sError, int iReturnValues )
+bool LuaHelpers::RunScript( Lua *L, const CString &sScript, const CString &sName, CString &sError, int iReturnValues, bool bSandbox )
 {
        // load string
        {
@@ -301,14 +307,14 @@ bool LuaHelpers::RunScript( Lua *L, const CString &sScript, const CString &sName
        }
 
        // evaluate
-       return LuaHelpers::RunScriptOnStack( L, sError, 0, iReturnValues );
+       return LuaHelpers::RunScriptOnStack( L, sError, 0, iReturnValues, bSandbox );
 }
 
 
-bool LuaHelpers::RunScript( Lua *L, const CString &sExpression, const CString &sName, int iReturnValues )
+bool LuaHelpers::RunScript( Lua *L, const CString &sExpression, const CString &sName, int iReturnValues, bool bSandbox )
 {
        CString sError;
-       if( !LuaHelpers::RunScript( L, sExpression, sName.size()? sName:CString("in"), sError, iReturnValues ) )
+       if( !LuaHelpers::RunScript( L, sExpression, sName.size()? sName:CString("in"), sError, iReturnValues, bSandbox ) )
        {
                sError = ssprintf( "Lua runtime error parsing \"%s\": %s", sName.size()? sName.c_str():sExpression.c_str(), sError.c_str() );
                Dialog::OK( sError, "LUA_ERROR" );
index 56cf2b2..00f5141 100644 (file)
@@ -80,16 +80,17 @@ namespace LuaHelpers
        /* Run the function with arguments at the top of the stack, with the given
         * number of arguments.  The specified number of return values are left on
         * the Lua stack.  On error, nils are left on the stack, sError is set and
-        * false is returned. */
-       bool RunScriptOnStack( Lua *L, CString &sError, int iArgs = 0, int iReturnValues = 0 );
+        * false is returned. If bSandbox is true, the script is given a blank env
+        * to run in instead of the full Lua environment. */
+       bool RunScriptOnStack( Lua *L, CString &sError, int iArgs = 0, int iReturnValues = 0, bool bSandbox = false );
 
        /* Run a script with the given name.  Return values are left on the Lua stack.
         * Returns false on error, with sError set. */
-       bool RunScript( Lua *L, const CString &sScript, const CString &sName, CString &sError, int iReturnValues = 0 );
+       bool RunScript( Lua *L, const CString &sScript, const CString &sName, CString &sError, int iReturnValues = 0, bool bSandbox = false );
 
        /* Convenience: run a script with one return value, displaying an error on failure.
         * The return value is left on the Lua stack. */
-       bool RunScript( Lua *L, const CString &sExpression, const CString &sName = "", int iReturnValues = 0 );
+       bool RunScript( Lua *L, const CString &sExpression, const CString &sName = "", int iReturnValues = 0, bool bSandbox = false );
 
        bool RunScriptFile( const CString &sFile );
 
index 16ebaf2..6c817c5 100755 (executable)
@@ -144,7 +144,7 @@ void LuaExpression::Register()
 {
        Lua *L = LUA->Get();
 
-       if( !LuaHelpers::RunScript(L, m_sExpression, "expression", 1) )
+       if( !LuaHelpers::RunScript(L, m_sExpression, "expression", 1, m_bSandboxed) )
        {
                this->SetFromNil();
                LUA->Release( L );
@@ -183,18 +183,25 @@ CString LuaData::Serialize() const
        return sRet;
 }
 
-void LuaData::LoadFromString( const CString &s )
+void LuaData::LoadFromString( const CString &s, bool bSandbox )
 {
+       m_bSandboxed = bSandbox;
+
        Lua *L = LUA->Get();
 
        /* Restore the serialized data by evaluating it. */
        CString sError;
-       if( !LuaHelpers::RunScript( L, s, "serialization", sError, 1 ) )
+       if( !LuaHelpers::RunScript(L, s, "serialization", sError, 1, m_bSandboxed) )
        {
                /* Serialize() should never return an invalid script.  Drop the failed
-                * script into the log (it may be too big to pass to FAIL_M) and fail. */
+                * script into the log (it may be too big to pass to FAIL_M) and fail.
+                *
+                * Sandboxed scripts may fail due to attempting to escape the sandbox;
+                * log a warning, but don't fail in that case.  */
                LOG->Warn( "Unserialization of \"%s\" failed: %s", s.c_str(), sError.c_str() );
-               FAIL_M( "Unserialization failed" );
+
+               if( !m_bSandboxed )
+                       FAIL_M( "Unserialization failed" );
        }
 
        this->SetFromStack( L );
@@ -215,7 +222,7 @@ void LuaData::Register()
        if( !m_bWasSet )
                return;
 
-       LoadFromString( m_sSerializedData );
+       LoadFromString( m_sSerializedData, m_bSandboxed );
        m_sSerializedData.erase( m_sSerializedData.begin(), m_sSerializedData.end() );
 }
 
index acbdc56..a3e2c44 100755 (executable)
@@ -55,14 +55,20 @@ private:
 class LuaExpression: public LuaReference
 {
 public:
-       LuaExpression( const CString &sExpression = "" ) { if( sExpression != "" ) SetFromExpression( sExpression ); }
+       LuaExpression( const CString &sExpression = "", bool bSandbox = false )
+       {
+               m_bSandboxed = bSandbox;
+               if( sExpression != "" ) SetFromExpression( sExpression );
+       }
+
        void SetFromExpression( const CString &sExpression );
-       CString GetExpression() const { return m_sExpression; }
+       const CString& GetExpression() const { return m_sExpression; }
 protected:
        virtual void Register();
 
 private:
        CString m_sExpression;
+       bool m_bSandboxed;
 };
 
 /* Reference a trivially restorable Lua object (any object that Serialize can handle).
@@ -71,7 +77,7 @@ class LuaData: public LuaReference
 {
 public:
        virtual CString Serialize() const;
-       virtual void LoadFromString( const CString &s );
+       virtual void LoadFromString( const CString &s, bool bSandbox = false );
 
 protected:
        virtual void BeforeReset();
@@ -79,6 +85,7 @@ protected:
 
        CString m_sSerializedData;
        bool m_bWasSet;
+       bool m_bSandboxed;
 };
 
 class LuaTable: public LuaData
index 10f74d6..eb99ce7 100755 (executable)
@@ -1189,20 +1189,20 @@ void Profile::LoadGeneralDataFromNode( const XNode* pNode )
        pNode->GetChildValue( "TotalMines",                                             m_iTotalMines );
        pNode->GetChildValue( "TotalHands",                                             m_iTotalHands );
 
-       if( IsMachine() )
+       CString sData;
+       if( pNode->GetChildValue( "Data", sData ) )
        {
-               CString sData;
-               if( pNode->GetChildValue( "Data", sData ) )
+               /* IMPORTANT: sandbox this call. We're taking arbitrary
+                * Lua from a source whose chain of trust has been broken. */
+               m_SavedLuaData.LoadFromString( sData, true );
+
+               if( m_SavedLuaData.GetLuaType() != LUA_TTABLE )
                {
-                       m_SavedLuaData.LoadFromString( sData );
-                       if( m_SavedLuaData.GetLuaType() != LUA_TTABLE )
-                       {
-                               LOG->Warn( "Profile data did not evaluate to a table" );
-                               Lua *L = LUA->Get();
-                               lua_newtable( L );
-                               m_SavedLuaData.SetFromStack( L );
-                               LUA->Release( L );
-                       }
+                       LOG->Warn( "Profile data did not evaluate to a table" );
+                       Lua *L = LUA->Get();
+                       lua_newtable( L );
+                       m_SavedLuaData.SetFromStack( L );
+                       LUA->Release( L );
                }
        }