Greetings, in the past I wrote a sandboxing module loader for c++/ python. I am moving away from python.. I can't stand it actually. Call me blasphemous... I'm immune.. So this code is going to just find the trash..
Maybe it will be useful to someone else. Can't post it all, however, if you are trying to integrate python into a system in which you need to restrict access to "safe" modules (meaning, non-native code), and wish also to restrict a script's ability to access other scripts, may be useful to have a look at. Basically, it is modeled after java. Each instantiated script in your system will have a ModuleLoader. That module loader is in charge of enforcing restrictions. Sorry for not being able to post a full compilable segment. But this sure would have helped me to look at before I wrote it. -tim p.s. Man I hope this code is formatted okay after posting.. Will see I guess. Anyway. Cheers. -- .h /** * legal header - public domain * * ============================================================================ * * @author Timothy Prepscius */ #ifndef __SnowCrash_Script_Python_Internal_PScriptModuleLoader_h__ #define __SnowCrash_Script_Python_Internal_PScriptModuleLoader_h__ #include <Utilities/URL.h> #include <Utilities/Transporter.h> #include <Common/Script/Signature.h> #include <set> #include <map> #include <list> #define BOOST_PYTHON_STATIC_LIB #include <boost/python.hpp> namespace SnowCrash { namespace Script { namespace Python { namespace pInternal { class PModuleLoader { protected: static boost::python::object mainDictionary; static bool importLock; protected: typedef std::set<Utilities::URL> ModuleSources; ModuleSources moduleSources; typedef std::map<std::wstring, PyObject *> ModuleMap; typedef std::list<boost::python::object> ModuleList; ModuleMap moduleMap; // this is to ensure that modules are destroyed in reverse order of construction // because python doesn't seem to keep module references within modules ModuleList moduleList; PyObject *getCachedModule (const Utilities::URL &url); void setCachedModule (const Utilities::URL &url, PyObject *); PyObject *returningModule (PyObject *); PyObject *loadModule (Utilities::Transporter::BufferPtr buffer); Utilities::Transporter::BufferPtr loadCodeString (const Utilities::URL &url); PyObject *getModule(const Utilities::URL &name); PyObject *findModule(const std::wstring &name); typedef std::list<Utilities::URL> ModuleURLList; ModuleURLList modulesLoading; public: PModuleLoader (); virtual ~PModuleLoader(); void addAvailableSource (const Utilities::URL &); PyObject *loadModule (const char *name); PyObject *loadModule (const Common::Script::Signature &); static PyObject *__import__ ( const char *name, PyObject *globals = NULL, PyObject *locals = NULL, PyObject *fromlist = NULL, PyObject *level = NULL ); static void setMainDictionary (boost::python::object); static void setImportLock (bool lock); } ; } // namespace pInternal } // namespace Python } // namespace Script } // namespace SnowCrash #endif -- .cpp /** * legal header - public domain * * ============================================================================ * * @author Timothy Prepscius */ #include "Utilities/BaseInclude/CppInclude.h" #include "PModuleLoader.h" #include <Utilities/VFS.h> #include "Global/Utilities.h" #include "Script/Instance.h" #include "../../Monitor.h" #include "../pScript/PScript.h" #include "../Exception.h" #include <algorithm> // for PyAPI_FUNC(PyObject *) PyMarshal_ReadObjectFromString(char *, Py_ssize_t); #include "marshal.h" //----------------------------------------------------------------------------- using namespace SnowCrash::Script::Python::pInternal; using namespace SnowCrash::Script::Python; using namespace SnowCrash::Script; using namespace boost::python; // ============================================================================= object PModuleLoader::mainDictionary; bool PModuleLoader::importLock = true; void PModuleLoader::setMainDictionary (object object) { PModuleLoader::mainDictionary = object; } PyObject *PModuleLoader::loadModule (Utilities::Transporter::BufferPtr buffer) { PyObject *m=NULL, *co=NULL, *v=NULL; try { // read the object // this failed once! must investigate co = PyMarshal_ReadObjectFromString (buffer->getData()+8, buffer- >getSize()-8); if (!co) throw Python::Exception (); m = PyModule_New("_private_"); if (!m) throw Python::Exception (); // get the dictionary and fill in the neccessary values.. arbitrary to us. // notice the incref on the dictionary, if it is not there garbage collections dies a // miserable death object dict = object(handle<>(incref(PyModule_GetDict(m)))); dict["__builtins__"] = mainDictionary["__builtins__"]; object fname = object(handle<>(((PyCodeObject *)co)->co_filename)); std::string s = extract<std::string>(fname); dict["__file__"] = fname; // evaluate the code, I wonder what this returns. v = PyEval_EvalCode((PyCodeObject *)co, dict.ptr(), dict.ptr()); if (v) { decref (v); } else { throw Python::Exception(); } } catch (Python::Exception &e) { std::string pe = handleException (e); LogRelease (PModuleLoader::loadModule, "caught exception " << pe); } catch (...) { LogRelease (PModuleLoader::loadModule, "caught unknown exception."); } return m; } PyObject *PModuleLoader::returningModule (PyObject *module) { if (module) { moduleList.push_back (object(detail::borrowed_reference(module))); incref (module); return module; } Py_RETURN_NONE; } Utilities::Transporter::BufferPtr PModuleLoader::loadCodeString (const Utilities::URL &url) { std::wstring extension = L".pyc"; std::wstring urlString = url; if (urlString.find (extension) == -1) urlString += extension; Utilities::URL fileURL (urlString); Utilities::VFS::InFilePtr inFile = Utilities::VFS::SystemSingleton->getInFile ( Global::getVFSDataPath(fileURL) ); if (inFile) { int size = inFile->size(); char *buffer = new char[size]; inFile->read (buffer, size); return new Utilities::Transporter::AllocatorBuffer (buffer, size); } return NULL; } PModuleLoader::PModuleLoader () { } PModuleLoader::~PModuleLoader () { moduleMap.clear(); while (!moduleList.empty()) moduleList.pop_front(); } void PModuleLoader::addAvailableSource (const Utilities::URL &url) { moduleSources.insert (url); } PyObject *PModuleLoader::getCachedModule (const Utilities::URL &url) { std::wstring moduleName = url; // quick return if we have it ModuleMap::iterator i = moduleMap.find(moduleName); if (i != moduleMap.end()) return i->second; return NULL; } void PModuleLoader::setCachedModule (const Utilities::URL &url, PyObject *module) { std::wstring moduleName = url; moduleMap[moduleName] = module; } PyObject *PModuleLoader::getModule (const Utilities::URL &url) { // see if we have a cached module PyObject *module = getCachedModule (url); if (module) return module; // else try to load the codestring Utilities::Transporter::BufferPtr codeString = loadCodeString (url); if (codeString) { // try to load the module modulesLoading.push_front (url); module = loadModule (codeString); modulesLoading.pop_front (); } setCachedModule (url, module); return module; } PyObject *PModuleLoader::findModule (const std::wstring &name) { // first try to load the relative path from the module if (!modulesLoading.empty()) { const Utilities::URL &url = modulesLoading.front(); Utilities::URL urlWithName (url, name); PyObject *module = getModule (urlWithName); if (module) return module; } ModuleSources::iterator i; for (i=moduleSources.begin(); i!=moduleSources.end(); ++i) { const Utilities::URL &url = *i; Utilities::URL urlWithName = Utilities::URL::join(url, name); PyObject *module = getModule(urlWithName); if (module) return module; } return NULL; } PyObject *PModuleLoader::loadModule (const Common::Script::Signature &signature) { // path/file/class std::wstring invokation = signature.getInvokation(); // path/file std::wstring path = Utilities::File::getDirectory (invokation, false); // zipfile.zip/path/file Utilities::URL zipPath = Utilities::URL::join(signature.getURL (),path); return returningModule (getModule (zipPath)); } PyObject *PModuleLoader::loadModule (const char *name) { std::wstring path = Utilities::String::convert (name); std::replace (path.begin(), path.end(), L'.', L'/'); return returningModule(findModule (path)); } PyObject *PModuleLoader::__import__ ( const char *name, PyObject *globals, PyObject *locals, PyObject *fromlist, PyObject *level ) { LogDebug (Script::Python, "__import__ " << name); try { static char *allowedBuiltinModules[] = { "dD.*", "dD_*", NULL } ; bool allowed = !importLock; // check if it matches a given pattern int i; for (i=0; !allowed && allowedBuiltinModules[i]!=NULL; ++i) { char *check = allowedBuiltinModules[i]; int checkLength = strlen(check); // if there is a star at the end, match all thing that have the substring at the beginning if (check[checkLength-1] == '*') { if (strncmp (name, check, checkLength-1)==0) allowed = true; } else { if (strcmp (name, check)==0) allowed = true; } } // only import if it is one of ours, else, must be pure python code, // downloaded with the script if (allowed) { PyObject *module = PyImport_ImportModuleEx ((char *)name, globals, locals, fromlist); if (module) { incref (module); return module; } } } catch (Python::Exception &e) { handleException (e); } Script::Instance *script = Script::MonitorSingleton->getExecutingScript (); if (!script) Py_RETURN_NONE; Python::pScript::PScript *pscript = DynamicCastPtr(Python::pScript::PScript, script); return pscript->getModuleLoader()->loadModule (name); } void PModuleLoader::setImportLock (bool lock) { importLock = lock; } --- Also, for these things to be triggered, you need to override them in the main dictionary /** * legal header - public domain * * ============================================================================ * initials date comments * * @author Timothy Prepscius */ #include "Utilities/BaseInclude/CppInclude.h" #include "PPackage.h" #include "PAccessor.h" #include "PModuleLoader.h" #include "PRestricted.h" #include "PLog.h" #include "PSystem.h" #include "../Defines.h" using namespace SnowCrash::Script::Python::pInternal; using namespace boost::python; BOOST_PYTHON_MODULE(dD_internal) { class_<PLog>("Log", init<std::string>()) .def ("println", &PLog::println); class_<PSystem>("System", no_init) .def ("getClientTimeMS", &PSystem::getClientTimeMS) .staticmethod ("getClientTimeMS") .def ("getMetaverseTimeMS", &PSystem::getMetaverseTimeMS) .staticmethod ("getMetaverseTimeMS"); } BOOST_PYTHON_FUNCTION_OVERLOADS(import_overloads, PModuleLoader::__import__, 1, 5); BOOST_PYTHON_FUNCTION_OVERLOADS(compile_overloads, PRestricted::throwPermissionDeniedException, 3, 5); BOOST_PYTHON_FUNCTION_OVERLOADS(open_overloads, PRestricted::throwPermissionDeniedException, 1, 3); bool PPackage::registerNativeMethods () { if (! ( (PyImport_AppendInittab("dD_internal", initdD_internal) != -1) && PyImport_ImportModule ("dD_internal") )) return false; IMPLEMENT_PYTHON_CONVERTER (PAccessor); PModuleLoader::setImportLock (false); { object main = import("__main__"); object dict(main.attr("__dict__")); PModuleLoader::setMainDictionary (dict); object builtins (dict["__builtins__"]); scope within(builtins); def ("__import__", &PModuleLoader::__import__, import_overloads()); def ("compile", &PRestricted::throwPermissionDeniedException, compile_overloads()); def ("exit", &PRestricted::throwPermissionDeniedException, open_overloads()); def ("execfile", &PRestricted::throwPermissionDeniedException, open_overloads()); def ("file", &PRestricted::throwPermissionDeniedException, open_overloads()); def ("open", &PRestricted::throwPermissionDeniedException, open_overloads()); } PModuleLoader::setImportLock (true); return true; } bool PPackage::deregisterNativeMethods () { return true; } -- http://mail.python.org/mailman/listinfo/python-list