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

Reply via email to