diff --git a/Sources/Plasma/Apps/plClient/winmain.cpp b/Sources/Plasma/Apps/plClient/winmain.cpp index b8a0e5b2..6a0799b8 100644 --- a/Sources/Plasma/Apps/plClient/winmain.cpp +++ b/Sources/Plasma/Apps/plClient/winmain.cpp @@ -847,12 +847,7 @@ BOOL WinInit(HINSTANCE hInst, int nCmdShow) char windowName[256]; wchar productString[256]; -//#ifdef PLASMA_EXTERNAL_RELEASE -#if 0 // Show the full product string in external build window title until we roll it into the options dialog -eap StrCopy(productString, ProductLongName(), arrsize(productString)); -#else - ProductString(productString, arrsize(productString)); -#endif StrToAnsi(windowName, productString, arrsize(windowName)); // Create a window @@ -1581,21 +1576,20 @@ int WINAPI WinMain(HINSTANCE hInst, HINSTANCE hPrevInst, LPSTR lpCmdLine, int nC si.cb = sizeof(si); wchar cmdLine[MAX_PATH]; const wchar ** addrs; - unsigned count; if (!eventExists) // if it is missing, assume patcher wasn't launched { StrCopy(cmdLine, s_patcherExeName, arrsize(cmdLine)); - count = GetAuthSrvHostnames(&addrs); - if(count && AuthSrvHostnameOverride()) + GetAuthSrvHostnames(&addrs); + if(wcslen(addrs[0])) StrPrintf(cmdLine, arrsize(cmdLine), L"%ws /AuthSrv=%ws", cmdLine, addrs[0]); - count = GetFileSrvHostnames(&addrs); - if(count && FileSrvHostnameOverride()) + GetFileSrvHostnames(&addrs); + if(wcslen(addrs[0])) StrPrintf(cmdLine, arrsize(cmdLine), L"%ws /FileSrv=%ws", cmdLine, addrs[0]); - count = GetGateKeeperSrvHostnames(&addrs); - if(count && GateKeeperSrvHostnameOverride()) + GetGateKeeperSrvHostnames(&addrs); + if(wcslen(addrs[0])) StrPrintf(cmdLine, arrsize(cmdLine), L"%ws /GateKeeperSrv=%ws", cmdLine, addrs[0]); if(!CreateProcessW(NULL, cmdLine, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi)) diff --git a/Sources/Plasma/Apps/plPythonPack/PythonInterface.cpp b/Sources/Plasma/Apps/plPythonPack/PythonInterface.cpp index 8d38dfc7..9e79c646 100644 --- a/Sources/Plasma/Apps/plPythonPack/PythonInterface.cpp +++ b/Sources/Plasma/Apps/plPythonPack/PythonInterface.cpp @@ -41,6 +41,8 @@ void PythonInterface::initPython(std::string rootDir) // initialize the Python stuff // let Python do some intialization... Py_SetProgramName("plasma"); + Py_NoSiteFlag = 1; + Py_IgnoreEnvironmentFlag = 1; Py_Initialize(); // intialize any of our special plasma python modules @@ -142,7 +144,7 @@ hsBool PythonInterface::DumpObject(PyObject* pyobj, char** pickle, Int32* size) #if (PY_MAJOR_VERSION == 2) && (PY_MINOR_VERSION < 4) s = PyMarshal_WriteObjectToString(pyobj); #else - s = PyMarshal_WriteObjectToString(pyobj, 0); + s = PyMarshal_WriteObjectToString(pyobj, Py_MARSHAL_VERSION); #endif // did it actually do it? if ( s != NULL ) diff --git a/Sources/Plasma/Apps/plPythonPack/main.cpp b/Sources/Plasma/Apps/plPythonPack/main.cpp index c6a6974e..70d08735 100644 --- a/Sources/Plasma/Apps/plPythonPack/main.cpp +++ b/Sources/Plasma/Apps/plPythonPack/main.cpp @@ -200,7 +200,6 @@ void WritePythonFile(const char *fileName, const char* path, hsStream *s) if (chars_read > 0) { printf(errmsg); - printf("\n"); } } diff --git a/Sources/Plasma/FeatureLib/pfPython/cyPythonInterface.cpp b/Sources/Plasma/FeatureLib/pfPython/cyPythonInterface.cpp index 1eec0183..f3007143 100644 --- a/Sources/Plasma/FeatureLib/pfPython/cyPythonInterface.cpp +++ b/Sources/Plasma/FeatureLib/pfPython/cyPythonInterface.cpp @@ -31,6 +31,7 @@ You can contact Cyan Worlds, Inc. by email legal@cyan.com // only be one instance of this interface. // #include "cyPythonInterface.h" +#include "plPythonPack.h" #include "compile.h" #include "marshal.h" @@ -822,6 +823,165 @@ PyObject *pyErrorRedirector::New() PYTHON_CLASS_CHECK_IMPL(ptErrorRedirector, pyErrorRedirector) PYTHON_CLASS_CONVERT_FROM_IMPL(ptErrorRedirector, pyErrorRedirector) +///////////////////////////////////////////////////////////////////////////// +// PEP 302 Import Hook +///////////////////////////////////////////////////////////////////////////// +#ifndef BUILDING_PYPLASMA +struct ptImportHook +{ + PyObject_HEAD +}; + +// First three functions are just so I can be lazy +// and use the already existing macros to do my dirty +// work. I'm seriously lazy. + +static PyObject* ptImportHook_new(PyTypeObject* type, PyObject* args, PyObject*) +{ + ptImportHook* self = (ptImportHook*)type->tp_alloc(type, 0); + return (PyObject*)self; +} + +PYTHON_NO_INIT_DEFINITION(ptImportHook) + +static void ptImportHook_dealloc(ptImportHook *self) +{ + self->ob_type->tp_free((PyObject*)self); +} + +PYTHON_METHOD_DEFINITION(ptImportHook, find_module, args) +{ + char* module_name; + PyObject* module_path; + + if (!PyArg_ParseTuple(args, "s|O", &module_name, &module_path)) + { + PyErr_SetString(PyExc_TypeError, "find_module expects string, string"); + PYTHON_RETURN_ERROR; + } + + // If this is set, we can't do it. + if (PyString_Check(module_path)) + PYTHON_RETURN_NONE; + + if (PythonPack::IsItPythonPacked(module_name)) + { + Py_INCREF(self); + return (PyObject*)self; + } + else + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION(ptImportHook, load_module, args) +{ + char* module_name; + if (!PyArg_ParseTuple(args, "s", &module_name)) + { + PyErr_SetString(PyExc_TypeError, "load_module expects string"); + PYTHON_RETURN_ERROR; + } + + // Grab sys.__dict__ so we can get started + PyObject* sys_mod = PyImport_ImportModule("sys"); + PyObject* sys_dict = PyModule_GetDict(sys_mod); + + // We want to check sys.modules for the module first + // If it's not in there, we have to load the module + // and add it to the sys.modules dict for future reference, + // otherwise reload() will not work properly. + PyObject* result = nil; + PyObject* modules = PyDict_GetItemString(sys_dict, "modules"); + hsAssert(PyDict_Check(modules), "sys.modules is not a dict"); + + if (result = PyDict_GetItemString(modules, module_name)) + { + if (!PyModule_Check(result)) + { + hsAssert(false, "PEP 302 hook found module in sys.modules, but it isn't a module! O.o"); + result = nil; + PyErr_SetString(PyExc_TypeError, "module in sys.modules isn't a module"); + } + } + else + { + if (PyObject* pyc = PythonPack::OpenPythonPacked(module_name)) + { + result = PyImport_ExecCodeModule(module_name, pyc); + if (result == nil) + { + PyErr_Print(); + } + else + { + PyModule_AddObject(result, "__loader__", (PyObject*)self); + PyDict_SetItemString(modules, module_name, result); + } + } + else + PyErr_SetString(PyExc_ImportError, "module not found in python.pak"); + } + + if (result) + return result; + else + PYTHON_RETURN_ERROR; +} + +PYTHON_START_METHODS_TABLE(ptImportHook) + PYTHON_METHOD(ptImportHook, find_module, "Params: module_name,package_path\nChecks to see if a given module exists (NOTE: package_path is not used!)"), + PYTHON_METHOD(ptImportHook, load_module, "Params: module_name \\nReturns the module given by module_name, if it exists in python.pak"), +PYTHON_END_METHODS_TABLE; + +PYTHON_TYPE_START(ptImportHook) + 0, + "Plasma.ptImportHook", + sizeof(ptImportHook), /* tp_basicsize */ + 0, /* tp_itemsize */ + (destructor)ptImportHook_dealloc, /* tp_dealloc */ + 0, /* tp_print */ + 0, /* tp_getattr */ + 0, /* tp_setattr */ + 0, /* tp_compare */ + 0, /* tp_repr */ + 0, /* tp_as_number */ + 0, /* tp_as_sequence */ + 0, /* tp_as_mapping */ + 0, /* tp_hash */ + 0, /* tp_call */ + 0, /* tp_str */ + 0, /* tp_getattro */ + 0, /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */ + "PEP 302 Import Hook", /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + PYTHON_DEFAULT_METHODS_TABLE(ptImportHook), /* tp_methods */ + 0, /* tp_members */ + 0, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + PYTHON_DEFAULT_INIT(ptImportHook), /* tp_init */ + 0, /* tp_alloc */ + ptImportHook_new /* tp_new */ +PYTHON_TYPE_END; + +void ptImportHook_AddPlasmaClasses(PyObject* m) +{ + PYTHON_CLASS_IMPORT_START(m); + PYTHON_CLASS_IMPORT(m, ptImportHook); + PYTHON_CLASS_IMPORT_END(m); +} +#endif // BUILDING_PYPLASMA + ///////////////////////////////////////////////////////////////////////////// // // Function : initPython @@ -838,6 +998,8 @@ void PythonInterface::initPython() // initialize the Python stuff // let Python do some initialization... Py_SetProgramName("plasma"); + Py_NoSiteFlag = 1; + Py_IgnoreEnvironmentFlag = 1; Py_Initialize(); #if defined(HAVE_CYPYTHONIDE) && !defined(PLASMA_EXTERNAL_RELEASE) @@ -954,7 +1116,6 @@ void PythonInterface::initPython() return; } - Py_DECREF(sys_dict); Py_DECREF(path_list); std::vector methods; // this is temporary, for easy addition of new methods @@ -989,6 +1150,26 @@ void PythonInterface::initPython() getOutputAndReset(&error); } + #ifndef BUILDING_PYPLASMA + // Begin PEP 302 Import Hook stuff + // We need to create a ptImportHook object + ptImportHook* hook = PyObject_New(ptImportHook, &ptImportHook_type); + PyObject* metapath = PyDict_GetItemString(sys_dict, "meta_path"); + Py_INCREF(metapath); + + // Since PEP 302 is insane, let's be sure things are the way + // that we expect them to be. Silent failures != cool. + hsAssert(metapath != nil, "PEP 302: sys.__dict__['meta_path'] missing!"); + hsAssert(PyList_Check(metapath), "PEP 302: sys.__dict__['meta_path'] is not a list!"); + + // Now that we have meta_path, add our hook to the list + PyList_Append(metapath, (PyObject*)hook); + Py_DECREF(metapath); + // And we're done! +#endif // BUILDING_PYPLASMA + + Py_DECREF(sys_dict); + // initialize the PlasmaConstants module PyMethodDef noMethods = {NULL}; plasmaConstantsMod = Py_InitModule("PlasmaConstants", &noMethods); // it has no methods, just values @@ -1277,6 +1458,9 @@ void PythonInterface::AddPlasmaClasses() // AI pyCritterBrain::AddPlasmaClasses(plasmaMod); + + // Stupid thing + ptImportHook_AddPlasmaClasses(plasmaMod); } @@ -1907,7 +2091,7 @@ hsBool PythonInterface::DumpObject(PyObject* pyobj, char** pickle, Int32* size) #if (PY_MAJOR_VERSION == 2) && (PY_MINOR_VERSION < 4) s = PyMarshal_WriteObjectToString(pyobj); #else - s = PyMarshal_WriteObjectToString(pyobj, 0); + s = PyMarshal_WriteObjectToString(pyobj, Py_MARSHAL_VERSION); #endif // did it actually do it? diff --git a/Sources/Plasma/NucleusLib/pnNetBase/Private/pnNbSrvs.cpp b/Sources/Plasma/NucleusLib/pnNetBase/Private/pnNbSrvs.cpp index d603c41a..62accab6 100644 --- a/Sources/Plasma/NucleusLib/pnNetBase/Private/pnNbSrvs.cpp +++ b/Sources/Plasma/NucleusLib/pnNetBase/Private/pnNbSrvs.cpp @@ -88,12 +88,6 @@ void SetAuthSrvHostname (const wchar addr[]) { StrCopy(s_authAddrConsole, addr, arrsize(s_authAddrConsole)); } -//============================================================================ -bool AuthSrvHostnameOverride () { - - return s_authAddrConsole[0]; -} - //============================================================================ // File //============================================================================ @@ -109,12 +103,6 @@ void SetFileSrvHostname (const wchar addr[]) { StrCopy(s_fileAddrConsole, addr, arrsize(s_fileAddrConsole)); } -//============================================================================ -bool FileSrvHostnameOverride () { - - return s_fileAddrConsole[0]; -} - //============================================================================ // Csr //============================================================================ diff --git a/Sources/Plasma/NucleusLib/pnNetBase/Private/pnNbSrvs.h b/Sources/Plasma/NucleusLib/pnNetBase/Private/pnNbSrvs.h index ad3557fa..2887c5bb 100644 --- a/Sources/Plasma/NucleusLib/pnNetBase/Private/pnNbSrvs.h +++ b/Sources/Plasma/NucleusLib/pnNetBase/Private/pnNbSrvs.h @@ -75,11 +75,9 @@ enum ESrvType { unsigned GetAuthSrvHostnames (const wchar *** addrs); // returns addrCount void SetAuthSrvHostname (const wchar addr[]); -bool AuthSrvHostnameOverride (); unsigned GetFileSrvHostnames (const wchar *** addrs); // returns addrCount void SetFileSrvHostname (const wchar addr[]); -bool FileSrvHostnameOverride (); unsigned GetCsrSrvHostnames (const wchar *** addrs); // returns addrCount void SetCsrSrvHostname (const wchar addr[]); diff --git a/Sources/Plasma/PubUtilLib/plNetClient/plNetClientMgr.h b/Sources/Plasma/PubUtilLib/plNetClient/plNetClientMgr.h index cb5b7743..6f73f4da 100644 --- a/Sources/Plasma/PubUtilLib/plNetClient/plNetClientMgr.h +++ b/Sources/Plasma/PubUtilLib/plNetClient/plNetClientMgr.h @@ -34,6 +34,7 @@ You can contact Cyan Worlds, Inc. by email legal@cyan.com #include "plNetVoiceList.h" #include "plNetClientMsgHandler.h" #include "plNetClientStats.h" // STATS Counters +#include "plNetClientMsgScreener.h" #include "pnNetCommon/plNetApp.h" @@ -162,6 +163,7 @@ private: plNetTalkList fTalkList; // other players I'm talking to plNetClientMsgHandler fMsgHandler; + plNetClientMsgScreener fScreener; // recorder support plNetClientRecorder* fMsgRecorder; diff --git a/Sources/Plasma/PubUtilLib/plNetClient/plNetClientMgrSend.cpp b/Sources/Plasma/PubUtilLib/plNetClient/plNetClientMgrSend.cpp index e17f43bc..60768eb7 100644 --- a/Sources/Plasma/PubUtilLib/plNetClient/plNetClientMgrSend.cpp +++ b/Sources/Plasma/PubUtilLib/plNetClient/plNetClientMgrSend.cpp @@ -28,7 +28,6 @@ You can contact Cyan Worlds, Inc. by email legal@cyan.com #include "plNetClientMgr.h" #include "plCreatableIndex.h" #include "plNetObjectDebugger.h" -#include "plNetClientMsgScreener.h" #include "pnNetCommon/plSynchedObject.h" #include "pnNetCommon/plSDLTypes.h" @@ -220,8 +219,7 @@ int plNetClientMgr::ISendGameMessage(plMessage* msg) if (GetFlagsBit(kDisabled)) return hsOK; - static plNetClientMsgScreener screener; // make static so that there's only 1 log per session - if (!screener.AllowMessage(msg)) + if (!fScreener.AllowOutgoingMessage(msg)) { if (GetFlagsBit(kScreenMessages)) return hsOK; // filter out illegal messages diff --git a/Sources/Plasma/PubUtilLib/plNetClient/plNetClientMsgHandler.cpp b/Sources/Plasma/PubUtilLib/plNetClient/plNetClientMsgHandler.cpp index 563e413e..111e63c9 100644 --- a/Sources/Plasma/PubUtilLib/plNetClient/plNetClientMsgHandler.cpp +++ b/Sources/Plasma/PubUtilLib/plNetClient/plNetClientMsgHandler.cpp @@ -351,6 +351,12 @@ MSG_HANDLER_DEFN(plNetClientMsgHandler,plNetMsgGameMessage) nc->DebugMsg("Converting game msg future timeStamp, curT=%f, futT=%f", secs, timeStamp); } + // Do some basic security checks on the incoming message because + // we cannot nesecarily trust the server because the server trusts + // the remote client WAY too much. + if (!IGetNetClientMgr()->fScreener.AllowIncomingMessage(gameMsg)) + return hsOK; + plgDispatch::Dispatch()->MsgSend(gameMsg); // Debug diff --git a/Sources/Plasma/PubUtilLib/plNetClient/plNetClientMsgScreener.cpp b/Sources/Plasma/PubUtilLib/plNetClient/plNetClientMsgScreener.cpp index 2d1bc820..324780cf 100644 --- a/Sources/Plasma/PubUtilLib/plNetClient/plNetClientMsgScreener.cpp +++ b/Sources/Plasma/PubUtilLib/plNetClient/plNetClientMsgScreener.cpp @@ -23,9 +23,11 @@ You can contact Cyan Worlds, Inc. by email legal@cyan.com Mead, WA 99021 *==LICENSE==*/ +#include "plCreatableIndex.h" #include "plNetClientMsgScreener.h" #include "plNetLinkingMgr.h" +#include "pfMessage/pfKIMsg.h" #include "pnNetCommon/plNetApp.h" #include "pnMessage/plMessage.h" @@ -86,7 +88,7 @@ bool plNetClientMsgScreener::IIsSenderCCR(const plNetGameMember* gm) const // // return true if msg is allowed/accepted as a net msg // -bool plNetClientMsgScreener::AllowMessage(const plMessage* msg) const +bool plNetClientMsgScreener::AllowOutgoingMessage(const plMessage* msg) const { if (!msg) return false; @@ -96,14 +98,60 @@ bool plNetClientMsgScreener::AllowMessage(const plMessage* msg) const return true; if (ans==kNo) { - // WarningMsg("Quick-reject net propagated msg %s", msg->ClassName()); + WarningMsg("Rejected: (Outgoing) %s [Illegal Message]", msg->ClassName()); return false; } if (!IValidateMessage(msg)) { - // WarningMsg("Validation failed. Blocking net propagated msg %s", msg->ClassName()); + WarningMsg("Rejected: (Outgoing) %s [Validation Failed]", msg->ClassName()); return false; } return true; } + +// +// Return false for obvious hacked network messages +// This is all because we cannot trust Cyan's servers +// +bool plNetClientMsgScreener::AllowIncomingMessage(const plMessage* msg) const +{ + if (!msg) + return false; + + bool result = IScreenIncoming(msg); + if (!result) + WarningMsg("Rejected: (Incoming) %s", msg->ClassName()); + + return result; +} + +bool plNetClientMsgScreener::IScreenIncoming(const plMessage* msg) const +{ + // Why would you EVER send a RefMsg accross the network??? + if (plFactory::DerivesFrom(CLASS_INDEX_SCOPED(plRefMsg), msg->ClassIndex())) + return false; + + // Blacklist some obvious hacks here... + switch (msg->ClassIndex()) + { + case CLASS_INDEX_SCOPED(plAudioSysMsg): + // This message has a flawed read/write + return false; + case CLASS_INDEX_SCOPED(plConsoleMsg): + // Python remote code execution vunerability + return false; + case CLASS_INDEX_SCOPED(pfKIMsg): + { + // Only accept Chat Messages! + const pfKIMsg* ki = pfKIMsg::ConvertNoRef(msg); + if (ki->GetCommand() != pfKIMsg::kHACKChatMsg) + return false; + return true; + } + default: + // Default allow everything else, otherweise we + // might break something that we really shouldn't... + return true; + } +} diff --git a/Sources/Plasma/PubUtilLib/plNetClient/plNetClientMsgScreener.h b/Sources/Plasma/PubUtilLib/plNetClient/plNetClientMsgScreener.h index 9313b03c..316f8a30 100644 --- a/Sources/Plasma/PubUtilLib/plNetClient/plNetClientMsgScreener.h +++ b/Sources/Plasma/PubUtilLib/plNetClient/plNetClientMsgScreener.h @@ -41,11 +41,13 @@ protected: bool IIsLocalArmatureModKey(plKey key, const plNetGameMember* gm) const; bool IIsSenderCCR(const plNetGameMember* gm=nil) const; bool IAmClient() const { return true; } + bool IScreenIncoming(const plMessage* msg) const; public: plNetClientMsgScreener(); - bool AllowMessage(const plMessage* msg) const; + bool AllowOutgoingMessage(const plMessage* msg) const; + bool AllowIncomingMessage(const plMessage* msg) const; }; diff --git a/Sources/Plasma/PubUtilLib/plNetClientComm/plNetClientComm.cpp b/Sources/Plasma/PubUtilLib/plNetClientComm/plNetClientComm.cpp index f19eb046..29a003a8 100644 --- a/Sources/Plasma/PubUtilLib/plNetClientComm/plNetClientComm.cpp +++ b/Sources/Plasma/PubUtilLib/plNetClientComm/plNetClientComm.cpp @@ -986,7 +986,7 @@ void NetCommConnect () { if (!gDataServerLocal) { // if a console override was specified for a filesrv, connect directly to the fileserver rather than going through the gatekeeper - if(GetFileSrvHostnames(&addrs) && FileSrvHostnameOverride()) + if(GetFileSrvHostnames(&addrs) && wcslen(addrs[0])) { NetCliFileStartConnect(addrs, count); } diff --git a/Sources/Plasma/PubUtilLib/plStatusLog/plStatusLog.cpp b/Sources/Plasma/PubUtilLib/plStatusLog/plStatusLog.cpp index bd6cde7e..c830c3d5 100644 --- a/Sources/Plasma/PubUtilLib/plStatusLog/plStatusLog.cpp +++ b/Sources/Plasma/PubUtilLib/plStatusLog/plStatusLog.cpp @@ -376,13 +376,7 @@ bool plStatusLogMgr::DumpLogs( const wchar *newFolderName ) //// plStatusLog //////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////// -#if defined(PLASMA_EXTERNAL_RELEASE) && (BUILD_TYPE == BUILD_TYPE_LIVE) -// If this is an external live build then don't write log files -UInt32 plStatusLog::fLoggingOff = true; -#else UInt32 plStatusLog::fLoggingOff = false; -#endif - plStatusLog::plStatusLog( UInt8 numDisplayLines, const wchar *filename, UInt32 flags ) {