View Single Post
09-02-11, 05:26 PM   #6
tecu
An Aku'mai Servant
AddOn Author - Click to view addons
Join Date: Apr 2006
Posts: 30
hey guys. thought i'd share what i have so far, if anyone's interested.

this is test code, i still need to:
- create file path validation
- prevent bytecode from loading
- decide on what goes into the whitelist
- decide what libraries i want
- actually incorporate all of this into a class

but i thought it was worth sharing for MORE FEEDBACK. and hey maybe it'd help anyone else who is interested, too.

main.cpp:
Code:
// mylua_test-2: main.cpp

#include <lua5.1/lua.hpp>

#include <iostream>
#include <string>

using namespace std;

string whitelist[] = {
	"_G",
	"ipairs",
	"next",
	"pairs",
	"print",
	"tostring"
};

int num_whitelist = (int)( sizeof(whitelist) / sizeof(whitelist[0]) );

bool isGlobalOkay( const string& global )
{
	for( int i = 0; i < num_whitelist; i++ )
	{
		if( whitelist[i] == global )
			return true;
	}
	return false;
}

bool isFileOkay( const string& filename )
{
	// TODO: check to see that file path is 'okay'
	// TODO: check for bytecode (see: LUA_SIGNATURE)
	return true;
}

void scrubGlobalEnvironment( lua_State *state )
{
	lua_pushnil( state );	// first key
	while( lua_next( state, LUA_GLOBALSINDEX ) != 0 )
	{
		if( lua_type( state, -2 ) == LUA_TSTRING )
		{
			if( !isGlobalOkay( lua_tostring( state, -2 ) ) )
			{
				cout << "killing key: " << lua_tostring(state, -2) << endl;
				// stack: -2 = key, -1 = value
				lua_pop( state, 1 );
				// stack: -1 = key
				lua_pushvalue( state, -1 );
				// stack: -2 = key, -1 = key
				lua_pushnil( state );
				// stack: -3 = key, -2 = key, -1 = nil
				lua_rawset( state, LUA_GLOBALSINDEX );
				// stack: -1 = key
			}
			else
			{
				cout << "key " << lua_tostring(state, -2) << " okay" << endl;
				lua_pop( state, 1 ); // for lua_next
			}
		}
		else
		{
			cout << "what." << endl;
			// not a string.  this should never happen?  i think?
			lua_pop( state, 1 ); // for lua_next
		}
	}
}

lua_State *createRestrictedLuaState()
{
	// create new state
	lua_State *state = luaL_newstate();
	
	// load libraries, eg base:
	lua_pushcfunction( state, luaopen_base );
	lua_call( state, 0, 0);
	
	// nil globals
	scrubGlobalEnvironment( state );
	
	return state;
}

int main( int argc, const char* argv[] )
{
	lua_State *state = createRestrictedLuaState();
	
	if( argc > 1 )
	{
		string filename = argv[1];
		
		if( isFileOkay( filename ) )
		{
			luaL_loadfile( state, filename.c_str() );
			// TODO: MAGIC
			lua_pcall( state, 0, 0, 0 );
		}
	}
	
	lua_close( state );
	return 0;
}
test.lua:
Code:
#!/usr/bin/env lua

print'hello from test.lua'

print'_G:'
for k, v in pairs(_G) do
	print(k, v)
end
output of ./main test.lua
Code:
killing key: xpcall
killing key: coroutine
killing key: newproxy
key _G okay
killing key: gcinfo
key pairs okay
key ipairs okay
killing key: _VERSION
killing key: setfenv
killing key: unpack
key tostring okay
killing key: tonumber
killing key: pcall
killing key: getfenv
killing key: rawset
killing key: type
killing key: setmetatable
key next okay
killing key: select
killing key: assert
killing key: load
killing key: rawget
killing key: loadstring
killing key: getmetatable
killing key: rawequal
killing key: dofile
killing key: collectgarbage
key print okay
killing key: error
killing key: loadfile
hello from test.lua
_G:
_G	table: 0x8ac7d0
pairs	function: 0x8ad1a0
ipairs	function: 0x8ad100
tostring	function: 0x8ad960
next	function: 0x8ad6c0
print	function: 0x8adb00
it looks okay!

PS: the TODO: MAGIC is there to remind me that i may want to create a second, lua-based sandbox for each script i load so that my c++/lua interface is consistent.
  Reply With Quote