From 1657fa34f371a40696a6020afdeb44693c31c230 Mon Sep 17 00:00:00 2001 From: "locka%iol.ie" Date: Mon, 9 Dec 2002 21:04:22 +0000 Subject: [PATCH] Share pref settings between com connect and plugin. b=179573 r=dbradley@netscape.com sr=brendan@mozilla.org a=asa@mozilla.org --- .../activex/src/control/ControlSite.cpp | 270 ++++++---- .../browser/activex/src/control/ControlSite.h | 47 +- .../activex/src/plugin/LegacyPlugin.cpp | 462 ++++++++++++------ .../browser/activex/src/plugin/Makefile.in | 12 +- .../activex/src/plugin/PrefObserver.cpp | 161 ++++-- .../activex/src/plugin/XPCDocument.cpp | 6 +- .../browser/activex/src/plugin/XPConnect.cpp | 96 +++- .../browser/activex/src/plugin/XPConnect.h | 28 +- .../activex/src/plugin/nsAxSecurityPolicy.js | 201 ++++++++ js/src/xpconnect/idl/nsIDispatchSupport.idl | 53 +- js/src/xpconnect/src/XPCDispObject.cpp | 121 ++--- js/src/xpconnect/src/XPCDispPrivate.h | 7 +- .../xpconnect/src/XPCIDispatchExtension.cpp | 4 +- js/src/xpconnect/src/nsDispatchSupport.cpp | 253 +++++++++- 14 files changed, 1309 insertions(+), 412 deletions(-) create mode 100644 embedding/browser/activex/src/plugin/nsAxSecurityPolicy.js diff --git a/embedding/browser/activex/src/control/ControlSite.cpp b/embedding/browser/activex/src/control/ControlSite.cpp index 443b8c81694..68aed5e20ad 100644 --- a/embedding/browser/activex/src/control/ControlSite.cpp +++ b/embedding/browser/activex/src/control/ControlSite.cpp @@ -45,13 +45,136 @@ std::list CControlSite::m_cControlList; +class CDefaultControlSiteSecurityPolicy : public CControlSiteSecurityPolicy +{ + // Test if the specified class id implements the specified category + BOOL ClassImplementsCategory(const CLSID & clsid, const CATID &catid, BOOL &bClassExists); +public: + // Test if the class is safe to host + virtual BOOL IsClassSafeToHost(const CLSID & clsid); + // Test if the specified class is marked safe for scripting + virtual BOOL IsClassMarkedSafeForScripting(const CLSID & clsid, BOOL &bClassExists); + // Test if the instantiated object is safe for scripting on the specified interface + virtual BOOL IsObjectSafeForScripting(IUnknown *pObject, const IID &iid); +}; + +BOOL +CDefaultControlSiteSecurityPolicy::ClassImplementsCategory(const CLSID &clsid, const CATID &catid, BOOL &bClassExists) +{ + bClassExists = FALSE; + + // Test if there is a CLSID entry. If there isn't then obviously + // the object doesn't exist and therefore doesn't implement any category. + // In this situation, the function returns REGDB_E_CLASSNOTREG. + + CRegKey key; + if (key.Open(HKEY_CLASSES_ROOT, _T("CLSID"), KEY_READ) != ERROR_SUCCESS) + { + // Must fail if we can't even open this! + return FALSE; + } + LPOLESTR szCLSID = NULL; + if (FAILED(StringFromCLSID(clsid, &szCLSID))) + { + return FALSE; + } + USES_CONVERSION; + CRegKey keyCLSID; + LONG lResult = keyCLSID.Open(key, W2CT(szCLSID), KEY_READ); + CoTaskMemFree(szCLSID); + if (lResult != ERROR_SUCCESS) + { + // Class doesn't exist + return FALSE; + } + keyCLSID.Close(); + + // CLSID exists, so try checking what categories it implements + bClassExists = TRUE; + CIPtr(ICatInformation) spCatInfo; + HRESULT hr = CoCreateInstance(CLSID_StdComponentCategoriesMgr, NULL, CLSCTX_INPROC_SERVER, IID_ICatInformation, (LPVOID*) &spCatInfo); + if (spCatInfo == NULL) + { + // Must fail if we can't open the category manager + return FALSE; + } + + // See what categories the class implements + CIPtr(IEnumCATID) spEnumCATID; + if (FAILED(spCatInfo->EnumImplCategoriesOfClass(clsid, &spEnumCATID))) + { + // Can't enumerate classes in category so fail + return FALSE; + } + + // Search for matching categories + BOOL bFound = FALSE; + CATID catidNext = GUID_NULL; + while (spEnumCATID->Next(1, &catidNext, NULL) == S_OK) + { + if (::IsEqualCATID(catid, catidNext)) + { + return TRUE; + } + } + return FALSE; +} + +// Test if the class is safe to host +BOOL CDefaultControlSiteSecurityPolicy::IsClassSafeToHost(const CLSID & clsid) +{ + return TRUE; +} + +// Test if the specified class is marked safe for scripting +BOOL CDefaultControlSiteSecurityPolicy::IsClassMarkedSafeForScripting(const CLSID & clsid, BOOL &bClassExists) +{ + // Test the category the object belongs to + return ClassImplementsCategory(clsid, CATID_SafeForScripting, bClassExists); +} + +// Test if the instantiated object is safe for scripting on the specified interface +BOOL CDefaultControlSiteSecurityPolicy::IsObjectSafeForScripting(IUnknown *pObject, const IID &iid) +{ + if (!pObject) { + return FALSE; + } + // Ask the control if its safe for scripting + CComQIPtr spObjectSafety = pObject; + if (!spObjectSafety) + { + return FALSE; + } + + DWORD dwSupported = 0; // Supported options (mask) + DWORD dwEnabled = 0; // Enabled options + + // Assume scripting via IDispatch + if (FAILED(spObjectSafety->GetInterfaceSafetyOptions( + iid, &dwSupported, &dwEnabled))) + { + // Interface is not safe or failure. + return FALSE; + } + + // Test if safe for scripting + if (!(dwEnabled & dwSupported) & INTERFACESAFE_FOR_UNTRUSTED_CALLER) + { + return FALSE; + } + + return TRUE; +} + +/////////////////////////////////////////////////////////////////////////////// + // Constructor CControlSite::CControlSite() { NG_TRACE_METHOD(CControlSite::CControlSite); m_hWndParent = NULL; - m_clsid = CLSID_NULL; + m_CLSID = CLSID_NULL; m_bSetClientSiteFirst = FALSE; m_bVisibleAtRuntime = TRUE; memset(&m_rcObjectPos, 0, sizeof(m_rcObjectPos)); @@ -62,6 +185,8 @@ CControlSite::CControlSite() m_bWindowless = FALSE; m_bSupportWindowlessActivation = TRUE; m_bSafeForScriptingObjectsOnly = FALSE; + m_pDefaultSecurityPolicy = new CDefaultControlSiteSecurityPolicy; + m_pSecurityPolicy = m_pDefaultSecurityPolicy; // Initialise ambient properties m_nAmbientLocale = 0; @@ -89,80 +214,11 @@ CControlSite::~CControlSite() NG_TRACE_METHOD(CControlSite::~CControlSite); Detach(); m_cControlList.remove(this); + + if (m_pDefaultSecurityPolicy) + delete m_pDefaultSecurityPolicy; } -// Helper method checks whether a class implements a particular category -HRESULT CControlSite::ClassImplementsCategory(const CLSID &clsid, const CATID &catid) -{ - // Test if there is a CLSID entry. If there isn't then obviously - // the object doesn't exist and therefore doesn't implement any category. - // In this situation, the function returns REGDB_E_CLASSNOTREG. - - CRegKey key; - if (key.Open(HKEY_CLASSES_ROOT, _T("CLSID"), KEY_READ) != ERROR_SUCCESS) - { - // Must fail if we can't even open this! - return E_FAIL; - } - LPOLESTR szCLSID = NULL; - if (FAILED(StringFromCLSID(clsid, &szCLSID))) - { - return E_FAIL; - } - USES_CONVERSION; - CRegKey keyCLSID; - LONG lResult = keyCLSID.Open(key, W2CT(szCLSID), KEY_READ); - CoTaskMemFree(szCLSID); - if (lResult != ERROR_SUCCESS) - { - // Class doesn't exist - return REGDB_E_CLASSNOTREG; - } - keyCLSID.Close(); - - // CLSID exists, so try checking what categories it implements - - CIPtr(ICatInformation) spCatInfo; - HRESULT hr = CoCreateInstance(CLSID_StdComponentCategoriesMgr, NULL, CLSCTX_INPROC_SERVER, IID_ICatInformation, (LPVOID*) &spCatInfo); - if (spCatInfo == NULL) - { - // Must fail if we can't open the category manager - return E_FAIL; - } - - // See what categories the class implements - CIPtr(IEnumCATID) spEnumCATID; - if (FAILED(spCatInfo->EnumImplCategoriesOfClass(clsid, &spEnumCATID))) - { - // Can't enumerate classes in category so fail - return E_FAIL; - } - - // Search for matching categories - BOOL bFound = FALSE; - CATID catidNext = GUID_NULL; - while (spEnumCATID->Next(1, &catidNext, NULL) == S_OK) - { - if (::IsEqualCATID(catid, catidNext)) - { - bFound = TRUE; - } - } - if (!bFound) - { - return S_FALSE; - } - - return S_OK; -} - - -#if 0 -// For use when the SDK does not define it (which isn't the case these days) -static const CATID CATID_SafeForScripting = -{ 0x7DD95801, 0x9882, 0x11CF, { 0x9F, 0xA9, 0x00, 0xAA, 0x00, 0x6C, 0x42, 0xC4 } }; -#endif - // Create the specified control, optionally providing properties to initialise // it with and a name. HRESULT CControlSite::Create(REFCLSID clsid, PropertyList &pl, @@ -170,19 +226,26 @@ HRESULT CControlSite::Create(REFCLSID clsid, PropertyList &pl, { NG_TRACE_METHOD(CControlSite::Create); - m_clsid = clsid; + m_CLSID = clsid; m_ParameterList = pl; + // See if security policy will allow the control to be hosted + if (m_pSecurityPolicy && !m_pSecurityPolicy->IsClassSafeToHost(clsid)) + { + return E_FAIL; + } + // See if object is script safe BOOL checkForObjectSafety = FALSE; - if (m_bSafeForScriptingObjectsOnly) + if (m_pSecurityPolicy && m_bSafeForScriptingObjectsOnly) { - HRESULT hrClass = ClassImplementsCategory(clsid, CATID_SafeForScripting); - if (hrClass == REGDB_E_CLASSNOTREG && szCodebase) + BOOL bClassExists = FALSE; + BOOL bIsSafe = m_pSecurityPolicy->IsClassMarkedSafeForScripting(clsid, bClassExists); + if (!bClassExists && szCodebase) { // Class doesn't exist, so allow code below to fetch it } - else if (FAILED(hrClass)) + else if (!bIsSafe) { // The class is not flagged as safe for scripting, so // we'll have to create it to ask it if its safe. @@ -195,26 +258,8 @@ HRESULT CControlSite::Create(REFCLSID clsid, PropertyList &pl, HRESULT hr = CoCreateInstance(clsid, NULL, CLSCTX_ALL, IID_IUnknown, (void **) &spObject); if (SUCCEEDED(hr) && checkForObjectSafety) { - // The control was created but it didn't check out as safe so - // it must be asked if its safe for scripting. - CComQIPtr spObjectSafety = spObject; - if (!spObjectSafety) - { - return E_FAIL; - } - DWORD dwSupported = 0; // Supported options (mask) - DWORD dwEnabled = 0; // Enabled options - // Assume scripting via IDispatch - if (FAILED(spObjectSafety->GetInterfaceSafetyOptions( - __uuidof(IDispatch), &dwSupported, &dwEnabled))) - { - // Interface is not safe or failure. - return E_FAIL; - } - - // Test if safe for scripting - if (!(dwEnabled & dwSupported) & INTERFACESAFE_FOR_UNTRUSTED_CALLER) + if (!m_pSecurityPolicy->IsObjectSafeForScripting(spObject, __uuidof(IDispatch))) { return E_FAIL; } @@ -596,6 +641,37 @@ void CControlSite::SetAmbientUserMode(BOOL bUserMode) } } +/////////////////////////////////////////////////////////////////////////////// +// CControlSiteSecurityPolicy implementation + +// Test if the class is safe to host +BOOL CControlSite::IsClassSafeToHost(const CLSID & clsid) +{ + if (m_pSecurityPolicy) + return m_pSecurityPolicy->IsClassSafeToHost(clsid); + return TRUE; +} + +// Test if the specified class is marked safe for scripting +BOOL CControlSite::IsClassMarkedSafeForScripting(const CLSID & clsid, BOOL &bClassExists) +{ + if (m_pSecurityPolicy) + return m_pSecurityPolicy->IsClassMarkedSafeForScripting(clsid, bClassExists); + return TRUE; +} + +// Test if the instantiated object is safe for scripting on the specified interface +BOOL CControlSite::IsObjectSafeForScripting(IUnknown *pObject, const IID &iid) +{ + if (m_pSecurityPolicy) + return m_pSecurityPolicy->IsObjectSafeForScripting(pObject, iid); + return TRUE; +} + +BOOL CControlSite::IsObjectSafeForScripting(const IID &iid) +{ + return IsObjectSafeForScripting(m_spObject, iid); +} /////////////////////////////////////////////////////////////////////////////// // IServiceProvider implementation diff --git a/embedding/browser/activex/src/control/ControlSite.h b/embedding/browser/activex/src/control/ControlSite.h index d811dc6ad97..3b2041ee9f4 100644 --- a/embedding/browser/activex/src/control/ControlSite.h +++ b/embedding/browser/activex/src/control/ControlSite.h @@ -61,6 +61,21 @@ COM_INTERFACE_ENTRY(IBindStatusCallback) \ COM_INTERFACE_ENTRY(IWindowForBindingUI) + +// Class that defines the control's security policy with regards to +// what controls it hosts etc. + +class CControlSiteSecurityPolicy +{ +public: + // Test if the class is safe to host + virtual BOOL IsClassSafeToHost(const CLSID & clsid) = 0; + // Test if the specified class is marked safe for scripting + virtual BOOL IsClassMarkedSafeForScripting(const CLSID & clsid, BOOL &bClassExists) = 0; + // Test if the instantiated object is safe for scripting on the specified interface + virtual BOOL IsObjectSafeForScripting(IUnknown *pObject, const IID &iid) = 0; +}; + // // Class for hosting an ActiveX control // @@ -85,6 +100,7 @@ // pSite = NULL; class CControlSite : public CComObjectRootEx, + public CControlSiteSecurityPolicy, public IOleClientSite, public IOleInPlaceSiteWindowless, public IOleControlSite, @@ -135,9 +151,13 @@ protected: // Pointer to object's IOleInPlaceObjectWindowless interface CComQIPtr m_spIOleInPlaceObjectWindowless; // CLSID of the control - CLSID m_clsid; + CLSID m_CLSID; // Parameter list PropertyList m_ParameterList; + // Default security policy + CControlSiteSecurityPolicy *m_pDefaultSecurityPolicy; + // Pointer to the security policy + CControlSiteSecurityPolicy *m_pSecurityPolicy; // Binding variables // Flag indicating whether binding is in progress @@ -196,9 +216,6 @@ END_OLECOMMAND_TABLE() // List of controls static std::list m_cControlList; - // Helper method - static HRESULT ClassImplementsCategory(const CLSID &clsid, const CATID &catid); - // Returns the window used when processing ole commands HWND GetCommandTargetWindow() { @@ -234,6 +251,16 @@ END_OLECOMMAND_TABLE() { m_spContainer = pContainer; } + // Set the security policy object. Ownership of this object remains with the caller and the security + // policy object is meant to exist for as long as it is set here. + virtual void SetSecurityPolicy(CControlSiteSecurityPolicy *pSecurityPolicy) + { + m_pSecurityPolicy = pSecurityPolicy; + } + virtual CControlSiteSecurityPolicy *GetSecurityPolicy() const + { + return m_pSecurityPolicy; + } // Methods to set ambient properties virtual void SetAmbientUserMode(BOOL bUser); @@ -242,7 +269,7 @@ END_OLECOMMAND_TABLE() // Returns the object's CLSID virtual const CLSID &GetObjectCLSID() const { - return m_clsid; + return m_CLSID; } // Tests if the object is valid or not virtual BOOL IsObjectValid() const @@ -260,6 +287,16 @@ END_OLECOMMAND_TABLE() return m_bInPlaceActive; } +// CControlSiteSecurityPolicy + // Test if the class is safe to host + virtual BOOL IsClassSafeToHost(const CLSID & clsid); + // Test if the specified class is marked safe for scripting + virtual BOOL IsClassMarkedSafeForScripting(const CLSID & clsid, BOOL &bClassExists); + // Test if the instantiated object is safe for scripting on the specified interface + virtual BOOL IsObjectSafeForScripting(IUnknown *pObject, const IID &iid); + // Test if the instantiated object is safe for scripting on the specified interface + virtual BOOL IsObjectSafeForScripting(const IID &iid); + // IServiceProvider virtual HRESULT STDMETHODCALLTYPE QueryService(REFGUID guidService, REFIID riid, void** ppv); diff --git a/embedding/browser/activex/src/plugin/LegacyPlugin.cpp b/embedding/browser/activex/src/plugin/LegacyPlugin.cpp index 272313578b2..ca4d010cdb8 100644 --- a/embedding/browser/activex/src/plugin/LegacyPlugin.cpp +++ b/embedding/browser/activex/src/plugin/LegacyPlugin.cpp @@ -56,21 +56,20 @@ /////////////////////////////////////////////////////////////////////////////// -// These are constants to control certain behaviours +// These are constants to control certain default behaviours -// Flag determines if only controls marked safe for scripting will be instantiated -const BOOL kSafeForScriptingControlsOnly = FALSE; +#ifndef MOZ_ACTIVEX_PLUGIN_XPCONNECT +// Flag determines if controls should be created if they are not marked +// safe for scripting. +const BOOL kHostSafeControlsOnly = FALSE; // Flag determines if controls should be downloaded and installed if there is a // codebase specified -const BOOL kDownloadControlsIfMissing = TRUE; +const BOOL kDownloadControlsIfMissing = FALSE; +#endif + // Flag determines whether the plugin will complain to the user if a page // contains a control it cannot load const BOOL kDisplayErrorMessages = FALSE; -// Registry keys containing lists of controls that the plugin explicitly does -// or does not support. -const TCHAR *kControlsToAllowKey = _T("Software\\Mozilla\\ActiveX\\Whitelist\\CLSID"); -const TCHAR *kControlsToDenyKey = _T("Software\\Mozilla\\ActiveX\\Blacklist\\CLSID"); - /////////////////////////////////////////////////////////////////////////////// // CInstallControlProgress @@ -256,24 +255,90 @@ jref NPP_GetJavaClass(void) #define MIME_OLEOBJECT2 "application/oleobject" #define MIME_ACTIVESCRIPT "text/x-activescript" -NPError NewScript(const char *pluginType, - PluginInstanceData *pData, - uint16 mode, - int16 argc, - char *argn[], - char *argv[]) +enum MozAxPluginErrors +{ + MozAxErrorActiveScriptNotSupported, + MozAxErrorControlIsNotSafeForScripting, + MozAxErrorCouldNotCreateControl, +}; + +static void +ShowError(MozAxPluginErrors errorCode, const CLSID &clsid) +{ + if (!kDisplayErrorMessages) + return; + + const TCHAR *szMsg = NULL; + const unsigned long kBufSize = 256; + TCHAR szBuffer[kBufSize]; + + // TODO errors are hardcoded for now + switch (errorCode) + { + case MozAxErrorActiveScriptNotSupported: + szMsg = _T("ActiveScript not supported yet!"); + break; + case MozAxErrorControlIsNotSafeForScripting: + { + USES_CONVERSION; + LPOLESTR szClsid; + StringFromCLSID(clsid, &szClsid); + _sntprintf(szBuffer, kBufSize - 1, + _T("Could not create the control %s because it is not marked safe for scripting."), OLE2T(szClsid)); + CoTaskMemFree(szClsid); + szMsg = szBuffer; + } + break; + case MozAxErrorCouldNotCreateControl: + { + USES_CONVERSION; + LPOLESTR szClsid; + StringFromCLSID(clsid, &szClsid); + _sntprintf(szBuffer, kBufSize - 1, + _T("Could not create the control %s. Check that it has been installed on your computer " + "and that this page correctly references it."), OLE2T(szClsid)); + CoTaskMemFree(szClsid); + szMsg = szBuffer; + } + break; + } + szBuffer[kBufSize - 1] = TCHAR('\0'); + if (szMsg) + MessageBox(NULL, szMsg, _T("ActiveX Error"), MB_OK | MB_ICONWARNING); +} + +static NPError +NewScript(const char *pluginType, + PluginInstanceData *pData, + uint16 mode, + int16 argc, + char *argn[], + char *argv[]) { CActiveScriptSiteInstance *pScriptSite = NULL; CActiveScriptSiteInstance::CreateInstance(&pScriptSite); - // TODO support ActiveScript - if (kDisplayErrorMessages) - MessageBox(NULL, _T("ActiveScript not supported yet!"), NULL, MB_OK); + ShowError(MozAxErrorActiveScriptNotSupported, CLSID_NULL); return NPERR_GENERIC_ERROR; } -static BOOL WillHandleCLSID(const CLSID &clsid) +static BOOL +WillHandleCLSID(const CLSID &clsid) { +#if defined(MOZ_ACTIVEX_PLUGIN_XPCONNECT) && defined(XPC_IDISPATCH_SUPPORT) + // Ensure the control is safe for scripting + nsCOMPtr dispSupport = do_GetService(NS_IDISPATCH_SUPPORT_CONTRACTID); + if (!dispSupport) + return FALSE; + nsCID cid; + memcpy(&cid, &clsid, sizeof(nsCID)); + PRBool isSafe = PR_FALSE; + PRBool classExists = PR_FALSE; + dispSupport->IsClassSafeToHost(cid, &isSafe, &classExists); + if (classExists && !isSafe) + return FALSE; + return TRUE; +#else if (::IsEqualCLSID(clsid, CLSID_NULL)) { return FALSE; @@ -363,14 +428,225 @@ static BOOL WillHandleCLSID(const CLSID &clsid) } return TRUE; +#endif } -NPError NewControl(const char *pluginType, - PluginInstanceData *pData, - uint16 mode, - int16 argc, - char *argn[], - char *argv[]) +static NPError +CreateControl(const CLSID &clsid, PluginInstanceData *pData, PropertyList &pl, LPCOLESTR szCodebase) +{ + // Make sure we got a CLSID we can handle + if (!WillHandleCLSID(clsid)) + { + return NPERR_GENERIC_ERROR; + } + + pData->clsid = clsid; + + // Set flags to specify if it is allowed to host safe or unsafe controls + // and download them. + PRBool hostSafeControlsOnly; + PRBool downloadControlsIfMissing; +#if defined(MOZ_ACTIVEX_PLUGIN_XPCONNECT) && defined(XPC_IDISPATCH_SUPPORT) + PRUint32 hostingFlags = MozAxPlugin::PrefGetHostingFlags(); + if (hostingFlags & nsIActiveXSecurityPolicy::HOSTING_FLAGS_HOST_SAFE_OBJECTS && + !(hostingFlags & nsIActiveXSecurityPolicy::HOSTING_FLAGS_HOST_ALL_OBJECTS)) + { + hostSafeControlsOnly = TRUE; + } + else if (hostingFlags & nsIActiveXSecurityPolicy::HOSTING_FLAGS_HOST_ALL_OBJECTS) + { + hostSafeControlsOnly = FALSE; + } + else + { + // Plugin can host neither safe nor unsafe controls, so just return + // without creating anything. + return NPERR_GENERIC_ERROR; + } + if (hostingFlags & nsIActiveXSecurityPolicy::HOSTING_FLAGS_DOWNLOAD_CONTROLS) + { + downloadControlsIfMissing = PR_TRUE; + } + else + { + downloadControlsIfMissing = PR_FALSE; + } + // Ensure we can obtain the nsIDispatchSupport service + nsCOMPtr dispSupport = do_GetService(NS_IDISPATCH_SUPPORT_CONTRACTID); + if (!dispSupport) + { + return NPERR_GENERIC_ERROR; + } + // Now test if the CLSID is safe for scripting + PRBool classIsMarkedSafeForScripting = PR_FALSE; + if (hostSafeControlsOnly) + { + PRBool classExists = PR_FALSE; + PRBool isClassSafeForScripting = PR_FALSE; + nsCID cid; + memcpy(&cid, &clsid, sizeof(cid)); + if (NS_SUCCEEDED(dispSupport->IsClassMarkedSafeForScripting(cid, &classExists, &isClassSafeForScripting)) && + classExists && isClassSafeForScripting) + { + classIsMarkedSafeForScripting = PR_TRUE; + } + } +#else + hostSafeControlsOnly = kHostSafeControlsOnly; + downloadControlsIfMissing = kDownloadControlsIfMissing; +#endif + + // Create the control site + CControlSiteInstance *pSite = NULL; + CControlSiteInstance::CreateInstance(&pSite); + if (pSite == NULL) + { + return NPERR_GENERIC_ERROR; + } + + pSite->m_bSupportWindowlessActivation = FALSE; +#if defined(MOZ_ACTIVEX_PLUGIN_XPCONNECT) && defined(XPC_IDISPATCH_SUPPORT) + // We handle our own security further down + pSite->SetSecurityPolicy(NULL); + pSite->m_bSafeForScriptingObjectsOnly = FALSE; +#else + pSite->m_bSafeForScriptingObjectsOnly = hostSafeControlsOnly; +#endif + + pSite->AddRef(); + +#ifdef MOZ_ACTIVEX_PLUGIN_XPCONNECT + // Set up the service provider and container so the control can get hold + // of the IE DOM and other interfaces + CComPtr sp; + MozAxPlugin::GetServiceProvider(pData, &sp); + if (sp) + pSite->SetServiceProvider(sp); + CComQIPtr container = sp; + if (container) + pSite->SetContainer(container); +#endif + + // TODO check the object is installed and at least as recent as + // that specified in szCodebase + + // Create the object + HRESULT hr; + if (!downloadControlsIfMissing || !szCodebase) + { + hr = pSite->Create(clsid, pl); + } + else if (szCodebase) + { + CComObject *pProgress = NULL; + CComPtr spBindCtx; + CComPtr spOldBSC; + CComObject::CreateInstance(&pProgress); + pProgress->AddRef(); + CreateBindCtx(0, &spBindCtx); + RegisterBindStatusCallback(spBindCtx, dynamic_cast(pProgress), &spOldBSC, 0); + + hr = pSite->Create(clsid, pl, szCodebase, spBindCtx); + if (hr == MK_S_ASYNCHRONOUS) + { + pProgress->mNPP = pData->pPluginInstance; + pProgress->mBindingInProgress = TRUE; + pProgress->mResult = E_FAIL; + + // Spin around waiting for binding to complete + HANDLE hFakeEvent = ::CreateEvent(NULL, TRUE, FALSE, NULL); + while (pProgress->mBindingInProgress) + { + MSG msg; + // Process pending messages + while (::PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE)) + { + if (!::GetMessage(&msg, NULL, 0, 0)) + { + pProgress->mBindingInProgress = FALSE; + break; + } + ::TranslateMessage(&msg); + ::DispatchMessage(&msg); + } + if (!pProgress->mBindingInProgress) + break; + // Sleep for a bit or the next msg to appear + ::MsgWaitForMultipleObjects(1, &hFakeEvent, FALSE, 500, QS_ALLEVENTS); + } + ::CloseHandle(hFakeEvent); + + hr = pProgress->mResult; + if (SUCCEEDED(hr)) + { + hr = pSite->Create(clsid, pl); + } + } + if (pProgress) + { + RevokeBindStatusCallback(spBindCtx, dynamic_cast(pProgress)); + pProgress->Release(); + } + } + if (FAILED(hr)) + { + ShowError(MozAxErrorCouldNotCreateControl, clsid); + } +#if defined(MOZ_ACTIVEX_PLUGIN_XPCONNECT) && defined(XPC_IDISPATCH_SUPPORT) + if (SUCCEEDED(hr) && hostSafeControlsOnly && !classIsMarkedSafeForScripting) + { + CComPtr cpUnk; + pSite->GetControlUnknown(&cpUnk); + nsIID iidDispatch; + memcpy(&iidDispatch, &__uuidof(IDispatch), sizeof(nsIID)); + PRBool isObjectSafe = PR_FALSE; + if (!cpUnk || + NS_FAILED(dispSupport->IsObjectSafeForScripting( + reinterpret_cast(cpUnk.p), iidDispatch, &isObjectSafe)) || + !isObjectSafe) + { + pSite->Detach(); + hr = E_FAIL; + ShowError(MozAxErrorControlIsNotSafeForScripting, clsid); + // DROP THROUGH + } + } +#endif + + // Clean up if the control could not be created + if (FAILED(hr)) + { + pSite->Release(); + return NPERR_GENERIC_ERROR; + } + +#if defined(MOZ_ACTIVEX_PLUGIN_XPCONNECT) && defined(XPC_IDISPATCH_SUPPORT) + // Hook up the event sink + nsEventSinkInstance *pSink = NULL; + nsEventSinkInstance::CreateInstance(&pSink); + if (pSink) + { + pSink->AddRef(); + pSink->mPlugin = pData; + CComPtr control; + pSite->GetControlUnknown(&control); + pSink->SubscribeToEvents(control); + } + pData->pControlEventSink = pSink; +#endif + pData->nType = itControl; + pData->pControlSite = pSite; + + return NPERR_NO_ERROR; +} + +static NPError +NewControl(const char *pluginType, + PluginInstanceData *pData, + uint16 mode, + int16 argc, + char *argn[], + char *argv[]) { // Read the parameters CLSID clsid = CLSID_NULL; @@ -380,7 +656,7 @@ NPError NewControl(const char *pluginType, if (strcmp(pluginType, MIME_OLEOBJECT1) != 0 && strcmp(pluginType, MIME_OLEOBJECT2) != 0) { - clsid = xpc_GetCLSIDForType(pluginType); + clsid = MozAxPlugin::GetCLSIDForType(pluginType); } for (int16 i = 0; i < argc; i++) @@ -490,129 +766,7 @@ NPError NewControl(const char *pluginType, } } - // Make sure we got a CLSID we can handle - if (!WillHandleCLSID(clsid)) - { - return NPERR_GENERIC_ERROR; - } - - pData->clsid = clsid; - - // Create the control site - CControlSiteInstance *pSite = NULL; - CControlSiteInstance::CreateInstance(&pSite); - if (pSite == NULL) - { - return NPERR_GENERIC_ERROR; - } - pSite->m_bSafeForScriptingObjectsOnly = kSafeForScriptingControlsOnly; - pSite->m_bSupportWindowlessActivation = FALSE; - pSite->AddRef(); - -#ifdef MOZ_ACTIVEX_PLUGIN_XPCONNECT - CComPtr sp; - xpc_GetServiceProvider(pData, &sp); - if (sp) - pSite->SetServiceProvider(sp); - CComQIPtr container = sp; - if (container) - pSite->SetContainer(container); -#endif - - // TODO check the object is installed and at least as recent as - // that specified in szCodebase - - // Create the object - HRESULT hr; - if (!kDownloadControlsIfMissing || !codebase.m_str) - { - hr = pSite->Create(clsid, pl); - } - else if (codebase.m_str) - { - CComObject *pProgress = NULL; - CComPtr spBindCtx; - CComPtr spOldBSC; - CComObject::CreateInstance(&pProgress); - pProgress->AddRef(); - CreateBindCtx(0, &spBindCtx); - RegisterBindStatusCallback(spBindCtx, dynamic_cast(pProgress), &spOldBSC, 0); - - hr = pSite->Create(clsid, pl, codebase, spBindCtx); - if (hr == MK_S_ASYNCHRONOUS) - { - pProgress->mNPP = pData->pPluginInstance; - pProgress->mBindingInProgress = TRUE; - pProgress->mResult = E_FAIL; - - // Spin around waiting for binding to complete - HANDLE hFakeEvent = ::CreateEvent(NULL, TRUE, FALSE, NULL); - while (pProgress->mBindingInProgress) - { - MSG msg; - // Process pending messages - while (::PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE)) - { - if (!::GetMessage(&msg, NULL, 0, 0)) - { - pProgress->mBindingInProgress = FALSE; - break; - } - ::TranslateMessage(&msg); - ::DispatchMessage(&msg); - } - if (!pProgress->mBindingInProgress) - break; - // Sleep for a bit or the next msg to appear - ::MsgWaitForMultipleObjects(1, &hFakeEvent, FALSE, 500, QS_ALLEVENTS); - } - ::CloseHandle(hFakeEvent); - - hr = pProgress->mResult; - if (SUCCEEDED(hr)) - { - hr = pSite->Create(clsid, pl); - } - } - if (pProgress) - { - RevokeBindStatusCallback(spBindCtx, dynamic_cast(pProgress)); - pProgress->Release(); - } - } - if (FAILED(hr)) - { - if (kDisplayErrorMessages) - { - USES_CONVERSION; - LPOLESTR szClsid; - StringFromCLSID(clsid, &szClsid); - TCHAR szBuffer[256]; - _stprintf(szBuffer, _T("Could not create the control %s. Check that it has been installed on your computer and that this page correctly references it."), OLE2T(szClsid)); - MessageBox(NULL, szBuffer, _T("ActiveX Error"), MB_OK | MB_ICONWARNING); - CoTaskMemFree(szClsid); - } - pSite->Release(); - return NPERR_GENERIC_ERROR; - } - -#ifdef XPC_IDISPATCH_SUPPORT - nsEventSinkInstance *pSink = NULL; - nsEventSinkInstance::CreateInstance(&pSink); - if (pSink) - { - pSink->AddRef(); - pSink->mPlugin = pData; - CComPtr control; - pSite->GetControlUnknown(&control); - pSink->SubscribeToEvents(control); - } - pData->pControlEventSink = pSink; -#endif - pData->nType = itControl; - pData->pControlSite = pSite; - - return NPERR_NO_ERROR; + return CreateControl(clsid, pData, pl, codebase.m_str); } @@ -651,7 +805,7 @@ NPError NP_LOADDS NPP_New(NPMIMEType pluginType, // Create a plugin according to the mime type #ifdef MOZ_ACTIVEX_PLUGIN_XPCONNECT - xpc_AddRef(); + MozAxPlugin::AddRef(); #endif NPError rv = NPERR_GENERIC_ERROR; @@ -674,7 +828,7 @@ NPError NP_LOADDS NPP_New(NPMIMEType pluginType, free(pData->szUrl); delete pData; #ifdef MOZ_ACTIVEX_PLUGIN_XPCONNECT - xpc_Release(); + MozAxPlugin::Release(); #endif return rv; } @@ -709,7 +863,7 @@ NPP_Destroy(NPP instance, NPSavedData** save) pSite->Detach(); pSite->Release(); } -#ifdef XPC_IDISPATCH_SUPPORT +#if defined(MOZ_ACTIVEX_PLUGIN_XPCONNECT) && defined(XPC_IDISPATCH_SUPPORT) if (pData->pControlEventSink) { pData->pControlEventSink->UnsubscribeFromEvents(); @@ -734,7 +888,7 @@ NPP_Destroy(NPP instance, NPSavedData** save) free(pData->szContentType); delete pData; #ifdef MOZ_ACTIVEX_PLUGIN_XPCONNECT - xpc_Release(); + MozAxPlugin::Release(); #endif instance->pdata = 0; @@ -971,7 +1125,7 @@ NPP_GetValue(NPP instance, NPPVariable variable, void *value) { NPError rv = NPERR_GENERIC_ERROR; #ifdef MOZ_ACTIVEX_PLUGIN_XPCONNECT - rv = xpc_GetValue(instance, variable, value); + rv = MozAxPlugin::GetValue(instance, variable, value); #endif return rv; } diff --git a/embedding/browser/activex/src/plugin/Makefile.in b/embedding/browser/activex/src/plugin/Makefile.in index 56d3effff2d..f0ad50ac4e5 100644 --- a/embedding/browser/activex/src/plugin/Makefile.in +++ b/embedding/browser/activex/src/plugin/Makefile.in @@ -119,7 +119,6 @@ CPPSRCS = \ StdAfx.cpp \ LegacyPlugin.cpp \ MozActiveX.cpp \ - PrefObserver.cpp \ npwin.cpp \ $(_CONTROL_CPPSRCS) \ $(NULL) @@ -133,6 +132,10 @@ CPPSRCS += LiveConnect.cpp CSRCS += javastubs.c endif +ifdef XPC_IDISPATCH_SUPPORT +CPPSRCS += PrefObserver.cpp +endif + LOCAL_INCLUDES = -I$(MOZCTLSRC) ifdef MOZ_ACTIVEX_PLUGIN_LIVECONNECT @@ -198,6 +201,9 @@ install-class: MozAxPlugin.class install-typelib: $(XPIDL_GEN_DIR)/nsIMozAxPlugin.xpt $(INSTALL) $< $(DIST)/bin/plugins +install-securitypolicy: nsAxSecurityPolicy.js + $(INSTALL) $< $(DIST)/bin/components + libs:: install-plugin ifdef MOZ_ACTIVEX_PLUGIN_LIVECONNECT @@ -208,6 +214,10 @@ ifdef MOZ_ACTIVEX_PLUGIN_XPCONNECT libs:: install-typelib endif +ifdef XPC_IDISPATCH_SUPPORT +libs:: install-securitypolicy +endif + ## Note: Ensure you create the redist dir containing the correct runtime dlls xpi:: install.js $(SHARED_LIBRARY) diff --git a/embedding/browser/activex/src/plugin/PrefObserver.cpp b/embedding/browser/activex/src/plugin/PrefObserver.cpp index 7247a9cc4e7..27be4219ec8 100644 --- a/embedding/browser/activex/src/plugin/PrefObserver.cpp +++ b/embedding/browser/activex/src/plugin/PrefObserver.cpp @@ -44,10 +44,25 @@ #include "npapi.h" #include "nsIServiceManagerUtils.h" #include "nsISupportsUtils.h" -#include "nsWeakReference.h" -#include "nsIObserver.h" #include "nsIPrefService.h" #include "nsIPrefBranchInternal.h" +#include "nsWeakReference.h" +#include "nsIObserver.h" +#include "nsCRT.h" +#include "nsString.h" + +#include "XPConnect.h" + +// These are the default hosting flags in the absence of a pref. + +const PRUint32 kDefaultHostingFlags = +#ifdef XPC_IDISPATCH_SUPPORT + nsIActiveXSecurityPolicy::HOSTING_FLAGS_HOST_NOTHING; +#else + nsIActiveXSecurityPolicy::HOSTING_FLAGS_HOST_SAFE_OBJECTS | + nsIActiveXSecurityPolicy::HOSTING_FLAGS_DOWNLOAD_CONTROLS | + nsIActiveXSecurityPolicy::HOSTING_FLAGS_SCRIPT_SAFE_OBJECTS; +#endif class PrefObserver : public nsSupportsWeakReference, @@ -55,23 +70,38 @@ class PrefObserver : { public: PrefObserver(); + protected: virtual ~PrefObserver(); - static PrefObserver *sPrefObserver; + void Sync(nsIPrefBranch *aPrefBranch); + + PRUint32 mHostingFlags; + nsCOMPtr mPrefService; public: NS_DECL_ISUPPORTS NS_DECL_NSIOBSERVER - static nsresult Subscribe(); - static nsresult Unsubscribe(); + static PrefObserver *sPrefObserver; + + nsresult Subscribe(); + nsresult Unsubscribe(); + PRUint32 GetHostingFlags() const; }; +const char *kActiveXHostingFlags = "security.xpconnect.activex."; +const char *kUserAgentPref = "general.useragent."; +const char *kProxyPref = "network.http."; + PrefObserver *PrefObserver::sPrefObserver = nsnull; -PrefObserver::PrefObserver() +PrefObserver::PrefObserver() : + mHostingFlags(kDefaultHostingFlags) { NS_INIT_ISUPPORTS(); + nsresult rv = NS_OK; + mPrefService = do_GetService(NS_PREFSERVICE_CONTRACTID, &rv); + NS_ASSERTION(mPrefService, "where is the pref service?"); } PrefObserver::~PrefObserver() @@ -89,38 +119,65 @@ NS_INTERFACE_MAP_END /* void observe (in nsISupports aSubject, in string aTopic, in wstring aData); */ NS_IMETHODIMP PrefObserver::Observe(nsISupports *aSubject, const char *aTopic, const PRUnichar *aData) { + if (nsCRT::strcmp(NS_PREFBRANCH_PREFCHANGE_TOPIC_ID, aTopic) != 0) + { + return S_OK; + } + + nsresult rv; + nsCOMPtr prefBranch = do_QueryInterface(aSubject, &rv); + if (NS_FAILED(rv)) + return rv; + + nsCAutoString pref = NS_ConvertUCS2toUTF8(aData); + if (nsCRT::strcmp(kActiveXHostingFlags, pref.get()) == 0 || + nsCRT::strcmp(kUserAgentPref, pref.get()) == 0 || + nsCRT::strcmp(kProxyPref, pref.get()) == 0) + { + Sync(prefBranch); + } + return NS_OK; +} + +void PrefObserver::Sync(nsIPrefBranch *aPrefBranch) +{ + NS_ASSERTION(aPrefBranch, "no pref branch"); + if (!aPrefBranch) + { + return; + } + + // TODO // const char *userAgent = NPN_UserAgent(mData->pPluginInstance); // ::UrlMkSetSessionOption(URLMON_OPTION_USERAGENT, userAgent, strlen(userAgent), 0); + + // TODO // INTERNET_PROXY_INFO ipi; - // UrlMkSetSessionOption(INTERNET_OPTION_PROXY, ....); - return NS_OK; + // ::UrlMkSetSessionOption(INTERNET_OPTION_PROXY, ....); + + nsCOMPtr dispSupport = do_GetService(NS_IDISPATCH_SUPPORT_CONTRACTID); + if (!dispSupport) + mHostingFlags = kDefaultHostingFlags; + else + dispSupport->GetHostingFlags(nsnull, &mHostingFlags); } nsresult PrefObserver::Subscribe() { - if (sPrefObserver) - { - return NS_OK; - } + NS_ENSURE_TRUE(mPrefService, NS_ERROR_FAILURE); + nsresult rv; + nsCOMPtr prefBranch = do_QueryInterface(mPrefService, &rv); + if (NS_FAILED(rv)) return rv; - sPrefObserver = new PrefObserver(); - NS_ENSURE_TRUE(sPrefObserver, NS_ERROR_OUT_OF_MEMORY); - sPrefObserver->AddRef(); + nsCOMPtr prefInternal = do_QueryInterface(prefBranch, &rv); + if (NS_FAILED(rv)) return rv; - nsresult rv = NS_OK; - nsCOMPtr prefService = do_GetService(NS_PREFSERVICE_CONTRACTID, &rv); - NS_ENSURE_SUCCESS(rv, rv); + prefInternal->AddObserver(kProxyPref, this, PR_TRUE); + prefInternal->AddObserver(kUserAgentPref, this, PR_TRUE); + prefInternal->AddObserver(kActiveXHostingFlags, this, PR_TRUE); - nsCOMPtr prefBranch; - prefService->GetBranch(nsnull, getter_AddRefs(prefBranch)); - NS_ENSURE_TRUE(prefBranch, NS_ERROR_FAILURE); - - nsCOMPtr pbi = do_QueryInterface(prefBranch); - NS_ENSURE_TRUE(pbi, NS_ERROR_FAILURE); - - pbi->AddObserver("network.http.proxy.", sPrefObserver, PR_TRUE); - pbi->AddObserver("general.useragent.", sPrefObserver, PR_TRUE); + Sync(prefBranch); return S_OK; } @@ -128,10 +185,50 @@ PrefObserver::Subscribe() nsresult PrefObserver::Unsubscribe() { - if (sPrefObserver) - { - sPrefObserver->Release(); - sPrefObserver = nsnull; - } + NS_ENSURE_TRUE(mPrefService, NS_ERROR_FAILURE); + nsresult rv; + nsCOMPtr prefBranch = do_QueryInterface(mPrefService, &rv); + if (NS_FAILED(rv)) return rv; + + nsCOMPtr prefInternal = do_QueryInterface(prefBranch, &rv); + if (NS_FAILED(rv)) return rv; + + prefInternal->RemoveObserver(kProxyPref, this); + prefInternal->RemoveObserver(kUserAgentPref, this); + prefInternal->RemoveObserver(kActiveXHostingFlags, this); + return NS_OK; } + +PRUint32 PrefObserver::GetHostingFlags() const +{ + return mHostingFlags; +} + +/////////////////////////////////////////////////////////////////////////////// + +PRUint32 MozAxPlugin::PrefGetHostingFlags() +{ + if (!PrefObserver::sPrefObserver) + { + PrefObserver::sPrefObserver = new PrefObserver(); + if (!PrefObserver::sPrefObserver) + { + return nsIActiveXSecurityPolicy::HOSTING_FLAGS_HOST_NOTHING; + } + PrefObserver::sPrefObserver->AddRef(); + PrefObserver::sPrefObserver->Subscribe(); + } + return PrefObserver::sPrefObserver->GetHostingFlags(); +} + +void MozAxPlugin::ReleasePrefObserver() +{ + if (PrefObserver::sPrefObserver) + { + PrefObserver::sPrefObserver->Unsubscribe(); + PrefObserver::sPrefObserver->Release(); + PrefObserver::sPrefObserver = nsnull; + } +} + diff --git a/embedding/browser/activex/src/plugin/XPCDocument.cpp b/embedding/browser/activex/src/plugin/XPCDocument.cpp index 141b7a9ee53..bf3c579d39b 100644 --- a/embedding/browser/activex/src/plugin/XPCDocument.cpp +++ b/embedding/browser/activex/src/plugin/XPCDocument.cpp @@ -734,7 +734,7 @@ public: mBrowser(NULL), mData(NULL) { - xpc_AddRef(); + MozAxPlugin::AddRef(); } HRESULT Init(PluginInstanceData *pData) @@ -799,7 +799,7 @@ public: { mWindow->Release(); } - xpc_Release(); + MozAxPlugin::Release(); } BEGIN_COM_MAP(IEDocument) @@ -1703,7 +1703,7 @@ END_COM_MAP() }; -HRESULT xpc_GetServiceProvider(PluginInstanceData *pData, IServiceProvider **pSP) +HRESULT MozAxPlugin::GetServiceProvider(PluginInstanceData *pData, IServiceProvider **pSP) { // Note this should be called on the main NPAPI thread CComObject *pDoc = NULL; diff --git a/embedding/browser/activex/src/plugin/XPConnect.cpp b/embedding/browser/activex/src/plugin/XPConnect.cpp index 884c0f6b79f..cec8ad751c6 100644 --- a/embedding/browser/activex/src/plugin/XPConnect.cpp +++ b/embedding/browser/activex/src/plugin/XPConnect.cpp @@ -64,9 +64,6 @@ #include "nsIScriptEventManager.h" #include "jsapi.h" -#ifdef XPC_IDISPATCH_SUPPORT -#include "nsIDispatchSupport.h" -#endif #include "LegacyPlugin.h" #include "XPConnect.h" @@ -82,13 +79,13 @@ nsScriptablePeer::nsScriptablePeer() : { NS_INIT_ISUPPORTS(); NS_ASSERTION(mTearOff, "can't create tearoff"); - xpc_AddRef(); + MozAxPlugin::AddRef(); } nsScriptablePeer::~nsScriptablePeer() { delete mTearOff; - xpc_Release(); + MozAxPlugin::Release(); } NS_IMPL_ADDREF(nsScriptablePeer) @@ -760,10 +757,6 @@ nsEventSink::InternalInvoke(DISPID dispIdMember, REFIID riid, LCID lcid, WORD wF nsDependentString eventName(bstrName.m_str); - nsresult rv; - nsCOMPtr domdoc; - nsCOMPtr scriptElements; - // Fire the script event handler... nsCOMPtr window; NPN_GetValue(mPlugin->pPluginInstance, NPNVDOMWindow, (void *)&window); @@ -920,20 +913,28 @@ HRESULT STDMETHODCALLTYPE nsScriptablePeerTearOff::Invoke(DISPID dispIdMember, R static PRUint32 gInstances = 0; -void xpc_AddRef() +void MozAxPlugin::AddRef() { if (gInstances == 0) - XPCOMGlueStartup(nsnull); + { + XPCOMGlueStartup(nsnull); + MozAxPlugin::PrefGetHostingFlags(); // Initial call to set it up + } gInstances++; } -void xpc_Release() +void MozAxPlugin::Release() { if (--gInstances == 0) - XPCOMGlueShutdown(); + { +#ifdef XPC_IDISPATCH_SUPPORT + MozAxPlugin::ReleasePrefObserver(); +#endif + XPCOMGlueShutdown(); + } } -CLSID xpc_GetCLSIDForType(const char *mimeType) +CLSID MozAxPlugin::GetCLSIDForType(const char *mimeType) { if (mimeType == NULL) { @@ -954,7 +955,7 @@ CLSID xpc_GetCLSIDForType(const char *mimeType) ULONG nCount = (sizeof(szGUID) / sizeof(szGUID[0])) - 1; GUID guidValue = GUID_NULL; - if (keyMimeType.QueryValue(_T("CLSID"), szGUID, &nCount) == ERROR_SUCCESS && + if (keyMimeType.QueryValue(szGUID, _T("CLSID"), &nCount) == ERROR_SUCCESS && SUCCEEDED(::CLSIDFromString(T2OLE(szGUID), &guidValue))) { return guidValue; @@ -966,20 +967,16 @@ CLSID xpc_GetCLSIDForType(const char *mimeType) nsScriptablePeer * -xpc_GetPeerForCLSID(const CLSID &clsid) +MozAxPlugin::GetPeerForCLSID(const CLSID &clsid) { -#ifdef XPC_IDISPATCH_SUPPORT return new nsScriptablePeer(); -#else - return new nsScriptablePeer(); -#endif } void -xpc_GetIIDForCLSID(const CLSID &clsid, nsIID &iid) +MozAxPlugin::GetIIDForCLSID(const CLSID &clsid, nsIID &iid) { #ifdef XPC_IDISPATCH_SUPPORT - memcpy(&iid, &_uuidof(IDispatch), sizeof(iid)); + memcpy(&iid, &__uuidof(IDispatch), sizeof(iid)); #else iid = NS_GET_IID(nsIMozAxPlugin); #endif @@ -987,7 +984,7 @@ xpc_GetIIDForCLSID(const CLSID &clsid, nsIID &iid) // Called by NPP_GetValue to provide the scripting values NPError -xpc_GetValue(NPP instance, NPPVariable variable, void *value) +MozAxPlugin::GetValue(NPP instance, NPPVariable variable, void *value) { if (instance == NULL) { @@ -1002,6 +999,55 @@ xpc_GetValue(NPP instance, NPPVariable variable, void *value) return NPERR_GENERIC_ERROR; } + // Test if the object is allowed to be scripted + +#ifdef XPC_IDISPATCH_SUPPORT + PRUint32 hostingFlags = MozAxPlugin::PrefGetHostingFlags(); + if (hostingFlags & nsIActiveXSecurityPolicy::HOSTING_FLAGS_SCRIPT_SAFE_OBJECTS && + !(hostingFlags & nsIActiveXSecurityPolicy::HOSTING_FLAGS_SCRIPT_ALL_OBJECTS)) + { + // Ensure the object is safe for scripting on the specified interface + nsCOMPtr dispSupport = do_GetService(NS_IDISPATCH_SUPPORT_CONTRACTID); + if (!dispSupport) return NPERR_GENERIC_ERROR; + + PRBool isScriptable = PR_FALSE; + + // Test if the object says its safe for scripting on IDispatch + nsIID iid; + memcpy(&iid, &__uuidof(IDispatch), sizeof(iid)); + CComPtr controlUnk; + pData->pControlSite->GetControlUnknown(&controlUnk); + dispSupport->IsObjectSafeForScripting(reinterpret_cast(controlUnk.p), iid, &isScriptable); + + // Test if the class id says safe for scripting + if (!isScriptable) + { + PRBool classExists = PR_FALSE; + nsCID cid; + memcpy(&iid, &pData->clsid, sizeof(cid)); + dispSupport->IsClassMarkedSafeForScripting(cid, &classExists, &isScriptable); + } + if (!isScriptable) + { + return NPERR_GENERIC_ERROR; + } + } + else if (hostingFlags & nsIActiveXSecurityPolicy::HOSTING_FLAGS_SCRIPT_ALL_OBJECTS) + { + // Drop through since all objects are scriptable + } + else + { + return NPERR_GENERIC_ERROR; + } +#else + // Object *must* be safe + if (!pData->pControlSite->IsObjectSafeForScripting(__uuidof(IDispatch))) + { + return NPERR_GENERIC_ERROR; + } +#endif + // Happy happy fun fun - redefine some NPPVariable values that we might // be asked for but not defined by every PluginSDK @@ -1012,7 +1058,7 @@ xpc_GetValue(NPP instance, NPPVariable variable, void *value) { if (!pData->pScriptingPeer) { - nsScriptablePeer *peer = xpc_GetPeerForCLSID(pData->clsid); + nsScriptablePeer *peer = MozAxPlugin::GetPeerForCLSID(pData->clsid); if (peer) { peer->AddRef(); @@ -1030,7 +1076,7 @@ xpc_GetValue(NPP instance, NPPVariable variable, void *value) else if (variable == kVarScriptableIID) { nsIID *piid = (nsIID *) NPN_MemAlloc(sizeof(nsIID)); - xpc_GetIIDForCLSID(pData->clsid, *piid); + GetIIDForCLSID(pData->clsid, *piid); *((nsIID **) value) = piid; return NPERR_NO_ERROR; } diff --git a/embedding/browser/activex/src/plugin/XPConnect.h b/embedding/browser/activex/src/plugin/XPConnect.h index 36bb229864f..a7b03ae4907 100644 --- a/embedding/browser/activex/src/plugin/XPConnect.h +++ b/embedding/browser/activex/src/plugin/XPConnect.h @@ -41,8 +41,16 @@ #include +#ifdef XPC_IDISPATCH_SUPPORT +#include "nsIDispatchSupport.h" +#include "nsIActiveXSecurityPolicy.h" +#endif + +#include "nsID.h" +#include "nsCOMPtr.h" #include "nsIClassInfo.h" #include "nsIMozAxPlugin.h" +#include "nsIServiceManagerUtils.h" #include "ControlEventSink.h" @@ -133,12 +141,18 @@ public: typedef CComObject nsEventSinkInstance; #endif -extern void xpc_AddRef(); -extern void xpc_Release(); -extern CLSID xpc_GetCLSIDForType(const char *mimeType); -extern NPError xpc_GetValue(NPP instance, NPPVariable variable, void *value); -extern nsScriptablePeer *xpc_GetPeerForCLSID(const CLSID &clsid); -extern nsIID xpc_GetIIDForCLSID(const CLSID &clsid); -extern HRESULT xpc_GetServiceProvider(PluginInstanceData *pData, IServiceProvider **pSP); +namespace MozAxPlugin { + extern void AddRef(); + extern void Release(); + extern CLSID GetCLSIDForType(const char *mimeType); + extern NPError GetValue(NPP instance, NPPVariable variable, void *value); + extern nsScriptablePeer *GetPeerForCLSID(const CLSID &clsid); + extern void GetIIDForCLSID(const CLSID &clsid, nsIID &iid); + extern HRESULT GetServiceProvider(PluginInstanceData *pData, IServiceProvider **pSP); +#ifdef XPC_IDISPATCH_SUPPORT + extern PRUint32 PrefGetHostingFlags(); + extern void ReleasePrefObserver(); +#endif +} #endif \ No newline at end of file diff --git a/embedding/browser/activex/src/plugin/nsAxSecurityPolicy.js b/embedding/browser/activex/src/plugin/nsAxSecurityPolicy.js new file mode 100644 index 00000000000..2216eec48ee --- /dev/null +++ b/embedding/browser/activex/src/plugin/nsAxSecurityPolicy.js @@ -0,0 +1,201 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: NPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Netscape Public License + * Version 1.1 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * http://www.mozilla.org/NPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is mozilla.org code. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1998 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Adam Lock + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the NPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the NPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +const NS_IACTIVEXSECURITYPOLICY_CONTRACTID = + "@mozilla.org/nsactivexsecuritypolicy;1"; +const NS_IACTIVEXSECURITYPOLICY_CID = + Components.ID("{B9BDB43B-109E-44b8-858C-B68C6C3DF86F}"); + +const nsISupports = Components.interfaces.nsISupports; +const nsIObserver = Components.interfaces.nsIObserver; +const nsIActiveXSecurityPolicy = Components.interfaces.nsIActiveXSecurityPolicy; + +// TODO lockdown this value +const kDefaultGlobalHostingFlags = + nsIActiveXSecurityPolicy.HOSTING_FLAGS_HOST_SAFE_OBJECTS | + nsIActiveXSecurityPolicy.HOSTING_FLAGS_DOWNLOAD_CONTROLS | + nsIActiveXSecurityPolicy.HOSTING_FLAGS_SCRIPT_SAFE_OBJECTS; + +const kDefaultOtherHostingFlags = + nsIActiveXSecurityPolicy.HOSTING_FLAGS_HOST_NOTHING; + +var gPref = null; + +function addPrefListener(observer, domain) +{ + try { + if (gPref == null) + { + var prefService = Components.classes["@mozilla.org/preferences-service;1"] + .getService(Components.interfaces.nsIPrefService); + gPref = prefService.getBranch(null); + } + var pbi = gPref.QueryInterface(Components.interfaces.nsIPrefBranchInternal); + pbi.addObserver(domain, observer, false); + } catch(ex) { + dump("Failed to observe prefs: " + ex + "\n"); + } +} + +function AxSecurityPolicy() +{ + this.prefPart1 = "security.xpconnect.activex."; + this.prefPart2 = ".hosting_flags"; + this.globalPrefName = this.prefPart1 + "global" + this.prefPart2; + // TODO read from prefs + // addPrefListener(this, this.globalPrefName); + this.syncPrefs(); +} + +AxSecurityPolicy.prototype = { + syncPrefs: function() + { + this.globalHostingFlags = kDefaultGlobalHostingFlags; + // TODO read from prefs + /* + try { + if (gPref == null) { + var prefService = Components.classes["@mozilla.org/preferences-service;1"] + .getService(Components.interfaces.nsIPrefService); + gPref = prefService.getBranch(null); + } + this.globalHostingFlags = gPref.getIntPref(this.globalPrefName); + } + catch (ex) { + dump("Failed to read prefs from " + this.globalPrefName + " : " + ex + "\n"); + this.globalHostingFlags = kDefaultGlobalHostingFlags; + } + */ + }, + + // nsIActiveXSecurityPolicy + getHostingFlags: function(context) + { + var hostingFlags; + if (context == null || context == "global") { + hostingFlags = this.globalHostingFlags; + } + else { + // TODO read from prefs + /* + try { + var prefName = prefPart1 + context + prefPart2; + hostingFlags = gPref.getIntPref(prefName); + } + catch (ex) { + dump("Failed to read prefs from " + prefName + " : " + ex + "\n"); + hostingFlags = kDefaultOtherHostingFlags; + } + */ + hostingFlags = kDefaultOtherHostingFlags; + } + return hostingFlags; + }, + // nsIObserver + observe: function(subject, topic, prefName) + { + if (topic != "nsPref:changed") + return; + this.syncPrefs(); + + }, + // nsISupports + QueryInterface: function(iid) { + if (!iid.equals(nsISupports) && + !iid.equals(nsIActiveXSecurityPolicy) && + !iid.equals(nsIObserver)) + throw Components.results.NS_ERROR_NO_INTERFACE; + return this; + } +}; + +/* factory for AxSecurityPolicy */ +var AxSecurityPolicyFactory = { + createInstance: function (outer, iid) + { + if (outer != null) + throw Components.results.NS_ERROR_NO_AGGREGATION; + if (!iid.equals(nsISupports) && + !iid.equals(nsIActiveXSecurityPolicy) && + !iid.equals(nsIObserver)) + throw Components.results.NS_ERROR_INVALID_ARG; + return new AxSecurityPolicy(); + } +}; + + +var AxSecurityPolicyModule = { + registerSelf: function (compMgr, fileSpec, location, type) + { + debug("*** Registering axsecurity policy.\n"); + compMgr = compMgr.QueryInterface(Components.interfaces.nsIComponentRegistrar); + compMgr.registerFactoryLocation( + NS_IACTIVEXSECURITYPOLICY_CID , + "ActiveX Security Policy Service", + NS_IACTIVEXSECURITYPOLICY_CONTRACTID, + fileSpec, + location, + type); + }, + unregisterSelf: function(compMgr, fileSpec, location) + { + compMgr = compMgr.QueryInterface(Components.interfaces.nsIComponentRegistrar); + compMgr.unregisterFactoryLocation(NS_IACTIVEXSECURITYPOLICY_CID, fileSpec); + }, + getClassObject: function(compMgr, cid, iid) + { + if (cid.equals(NS_IACTIVEXSECURITYPOLICY_CID)) + return AxSecurityPolicyFactory; + + if (!iid.equals(Components.interfaces.nsIFactory)) + throw Components.results.NS_ERROR_NOT_IMPLEMENTED; + + throw Components.results.NS_ERROR_NO_INTERFACE; + }, + canUnload: function(compMgr) + { + return true; + } +}; + +/* entrypoint */ +function NSGetModule(compMgr, fileSpec) { + return AxSecurityPolicyModule; +} + + diff --git a/js/src/xpconnect/idl/nsIDispatchSupport.idl b/js/src/xpconnect/idl/nsIDispatchSupport.idl index 9c495644b58..4615037c61e 100644 --- a/js/src/xpconnect/idl/nsIDispatchSupport.idl +++ b/js/src/xpconnect/idl/nsIDispatchSupport.idl @@ -39,6 +39,9 @@ #include "nsIVariant.idl" %{ C++ +// Pull in jsval definition +#include "jspubtd.h" + // {40c4883d-079f-43db-82a9-df0a59d37998} #define NS_IDISPATCH_SUPPORT_CID \ { 0x40c4883d, 0x079f, 0x43db, \ @@ -62,24 +65,58 @@ interface IDispatch; interface nsIDispatchSupport : nsISupports { /** - * Converts a COM Variant to a jsval - * @param comvar The COM Variant to be converted - * @param val The jsval to receive the converted value + * Converts a COM Variant to a jsval. + * @param comvar The COM Variant to be converted. + * @param val The jsval to receive the converted value. */ void COMVariant2JSVal(in COMVARIANTPtr comvar, out JSVal val); + /** * Converts a jsval to a COM Variant - * @param var The jsval to be converted + * @param var The jsval to be converted. * @param comvar The COM Variant to receive the converted value */ void JSVal2COMVariant(in JSVal var, out COMVARIANT comvar); + /** - * Instantiate a COM component - * This checks to see if the component is scriptable. If it is, it instantiates it - * @param className contract ID or class ID of the desired component to instantiate + * Instantiate a COM component. + * @param className ProgID or Class ID of the desired object to instantiate * @return The IDispatch interface pointer if instantiated */ - IDispatch CreateInstance(in astring className, in PRBool testScriptability); + IDispatch createInstance(in astring className); + + /** + * Test if the class is safe to host. + * @param clsid The nsID representation of the CLSID to test. + * @param classExists Returns containing PR_FALSE if the class is + * not registered. + */ + boolean isClassSafeToHost(in nsCIDRef cid, out boolean classExists); + + /** + * Test if the specified class is marked safe for scripting. + * @param cid The nsID representation of the CLSID to test. + * @param classExists Returns containing PR_FALSE if the class is not + * registered. + */ + boolean isClassMarkedSafeForScripting(in nsCIDRef cid, out boolean classExists); + + /** + * Test if the instantiated object is safe for scripting on the specified + * interface. + * @param theObject The object to test (an IUnknown cast into a void *). + * @param iid The interface to test if it is safe for scripting on. + */ + boolean isObjectSafeForScripting(in voidPtr theObject, in nsIIDRef id); + + /** + * Return the ActiveX security and hosting flags. See nsIActiveXSecurityPolicy + * for list of flags. + * @param context The context for which flags are requested. At present the + * only valid value is nsnull. + */ + unsigned long getHostingFlags(in string aContext); + }; %{ C++ diff --git a/js/src/xpconnect/src/XPCDispObject.cpp b/js/src/xpconnect/src/XPCDispObject.cpp index 8ed34353c02..709ba07ff5b 100644 --- a/js/src/xpconnect/src/XPCDispObject.cpp +++ b/js/src/xpconnect/src/XPCDispObject.cpp @@ -41,6 +41,7 @@ * XPC_IDispatch_GetterSetter, and XPC_IDispatch_CallMethod */ #include "xpcprivate.h" +#include "nsIActiveXSecurityPolicy.h" /** * This is COM's IDispatch IID, but in XPCOM's nsID type @@ -72,88 +73,68 @@ XPCDispObject::WrapIDispatch(IDispatch *pDispatch, XPCCallContext &ccx, return PR_TRUE; } -/** - * Helper function to determine whether an object has the safe scripting - * category - * @param the class ID of the COM object to be created - * @return true if it has the category - */ -static PRBool HasSafeScriptingCategory(const CLSID & classID) +HRESULT XPCDispObject::COMCreateInstance(BSTR className, PRBool enforceSecurity, IDispatch ** result) { - // TODO: probably should look into caching this if this becomes - // a performance issue - CComPtr catInfo; - HRESULT hr = catInfo.CoCreateInstance(CLSID_StdComponentCategoriesMgr); - // Must fail if we can't open the category manager - if(!catInfo) - return PR_FALSE; - - // See what categories the class implements - CComPtr enumCATID; - if(FAILED(catInfo->EnumImplCategoriesOfClass(classID, &enumCATID))) - return PR_FALSE; // Can't enumerate classes in category so fail - - // Search for matching categories - CATID catidNext = GUID_NULL; - // Get the next category, and no, I don't know what the 1 is - while(enumCATID->Next(1, &catidNext, NULL) == S_OK) - { - if(::IsEqualCATID(CATID_SafeForScripting, catidNext)) - { - return PR_TRUE; - } - } - return PR_FALSE; -} + // Turn the string into a CLSID + _bstr_t bstrName(className); + CLSID classID = CLSID_NULL; + HRESULT hr = CLSIDFromString(bstrName, &classID); + if (FAILED(hr)) + hr = CLSIDFromProgID(bstrName, &classID); + if(FAILED(hr) || ::IsEqualCLSID(classID, CLSID_NULL)) + return hr; -/** - * Returns true if the desired scriptable flags are set - * @return true if the desired scriptable flags are set - */ -inline -PRBool ScriptOK(DWORD value) -{ - return value & (INTERFACESAFE_FOR_UNTRUSTED_CALLER | - INTERFACESAFE_FOR_UNTRUSTED_DATA); -} + nsresult rv; + nsCOMPtr dispSupport = do_GetService(NS_IDISPATCH_SUPPORT_CONTRACTID, &rv); + if (NS_FAILED(rv)) return E_UNEXPECTED; -HRESULT XPCDispObject::COMCreateInstance(BSTR className, PRBool testScriptability, IDispatch ** result) -{ - CLSID classID; - HRESULT hr; - // If this looks like a class ID - if(FAILED(CLSIDFromString(className, &classID))) + PRUint32 hostingFlags = nsIActiveXSecurityPolicy::HOSTING_FLAGS_HOST_NOTHING; + dispSupport->GetHostingFlags(nsnull, &hostingFlags); + + PRBool allowSafeObjects; + if (hostingFlags & (nsIActiveXSecurityPolicy::HOSTING_FLAGS_SCRIPT_SAFE_OBJECTS)) + allowSafeObjects = PR_TRUE; + else + allowSafeObjects = PR_FALSE; + PRBool allowAnyObjects; + if (hostingFlags & (nsIActiveXSecurityPolicy::HOSTING_FLAGS_SCRIPT_ALL_OBJECTS)) + allowAnyObjects = PR_TRUE; + else + allowAnyObjects = PR_FALSE; + + // There is no point proceeding if flags say we can't script safe or unsafe objects + if(enforceSecurity && !allowSafeObjects && !allowAnyObjects) { - hr = CLSIDFromProgID(className, &classID); - if(FAILED(hr)) - return hr; + return E_FAIL; + } + + // Test if the object is scriptable + PRBool isScriptable = PR_FALSE; + if(enforceSecurity && !allowAnyObjects) + { + nsCID cid; + memcpy(&cid, &classID, sizeof(nsCID)); + PRBool classExists = PR_FALSE; + dispSupport->IsClassMarkedSafeForScripting(cid, &classExists, &isScriptable); + if(!classExists) + return REGDB_E_CLASSNOTREG; } - PRBool scriptableOK = PR_TRUE; - if(testScriptability) - scriptableOK = HasSafeScriptingCategory(classID); - // Didn't have the safe for scripting category so lets look at IObjectSafety + // Create the object CComPtr disp; - HRESULT hResult = disp.CoCreateInstance(classID); - if(FAILED(hResult)) - return hResult; + hr = disp.CoCreateInstance(classID); + if(FAILED(hr)) + return hr; - // If we're testing scriptability and it didn't have a scripting category + // If we're enforcing security and it didn't have a scripting category // we'll check via the IObjectSafety interface - if(testScriptability && !scriptableOK) + if(enforceSecurity && !allowAnyObjects && !isScriptable) { - CComQIPtr objSafety(disp); - // Didn't have IObjectSafety so we'll bail - if(!objSafety) - return E_FAIL; - DWORD supported; - DWORD state; - hr = objSafety->GetInterfaceSafetyOptions(IID_IDispatch, &supported, &state); - if(FAILED(hr)) - return hr; - if(!ScriptOK(supported) || !ScriptOK(state)) + dispSupport->IsObjectSafeForScripting(disp, NSID_IDISPATCH, &isScriptable); + if (!isScriptable) return E_FAIL; } + // Copy and addref disp.CopyTo(result); return S_OK; diff --git a/js/src/xpconnect/src/XPCDispPrivate.h b/js/src/xpconnect/src/XPCDispPrivate.h index 3542c0b9207..26e479c5da1 100644 --- a/js/src/xpconnect/src/XPCDispPrivate.h +++ b/js/src/xpconnect/src/XPCDispPrivate.h @@ -1005,13 +1005,14 @@ public: * Instantiates a COM object given a class ID or a prog ID * @param className a prog ID or a class ID in the form of * {00000000-0000-0000-0000-000000000000} - * @param testScriptability if true, this tests the objects for scripting - * this tests the category and the IObjectSafety interface + * @param enforceSecurity if true, will apply checks to ensure + * the object can be created giving the current + * security settings. * @param result pointer to the pointer to receive the interface pointer */ static HRESULT COMCreateInstance(BSTR className, - PRBool testScriptability, IDispatch ** result); + PRBool enforceSecurity, IDispatch ** result); /** * Wraps an IDispatch interface, returning the object as a jsval * @param pDispatch IDispatch pointer diff --git a/js/src/xpconnect/src/XPCIDispatchExtension.cpp b/js/src/xpconnect/src/XPCIDispatchExtension.cpp index e8dee214a63..05d7387cc49 100644 --- a/js/src/xpconnect/src/XPCIDispatchExtension.cpp +++ b/js/src/xpconnect/src/XPCIDispatchExtension.cpp @@ -42,7 +42,7 @@ PRBool XPCIDispatchExtension::mIsEnabled = PR_TRUE; static JSBool CommonConstructor(XPCCallContext &ccx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval, PRBool testScriptability) + jsval *rval, PRBool enforceSecurity) { // Check if IDispatch is enabled, fail if not if (!nsXPConnect::IsIDispatchEnabled()) @@ -66,7 +66,7 @@ CommonConstructor(XPCCallContext &ccx, JSObject *obj, uintN argc, jsval *argv, } // Instantiate the desired COM object CComPtr pDispatch; - HRESULT rv = XPCDispObject::COMCreateInstance(bstrClassName, testScriptability, &pDispatch); + HRESULT rv = XPCDispObject::COMCreateInstance(bstrClassName, enforceSecurity, &pDispatch); if(FAILED(rv)) { XPCThrower::ThrowCOMError(ccx, rv, NS_ERROR_XPC_COM_CREATE_FAILED); diff --git a/js/src/xpconnect/src/nsDispatchSupport.cpp b/js/src/xpconnect/src/nsDispatchSupport.cpp index 30335b0b009..68394944bb1 100644 --- a/js/src/xpconnect/src/nsDispatchSupport.cpp +++ b/js/src/xpconnect/src/nsDispatchSupport.cpp @@ -43,19 +43,115 @@ #include "XPCPrivate.h" +#include "nsIActiveXSecurityPolicy.h" + +static nsresult +ClassIsListed(HKEY hkeyRoot, const TCHAR *szKey, const CLSID &clsid, PRBool &listIsEmpty) +{ + // Test if the specified CLSID is found in the specified registry key + + listIsEmpty = PR_FALSE; + + CRegKey keyList; + if(keyList.Open(hkeyRoot, szKey, KEY_READ) != ERROR_SUCCESS) + { + return NS_ERROR_FAILURE; + } + + // Enumerate CLSIDs looking for this one + int i = 0; + do { + USES_CONVERSION; + TCHAR szCLSID[64]; + const DWORD kBufLength = sizeof(szCLSID) / sizeof(szCLSID[0]); + if(::RegEnumKey(keyList, i, szCLSID, kBufLength) != ERROR_SUCCESS) + { + // An empty list + if(i == 0) + listIsEmpty = PR_TRUE; + break; + } + ++i; + szCLSID[kBufLength - 1] = TCHAR('\0'); + CLSID clsidToCompare = GUID_NULL; + if(SUCCEEDED(::CLSIDFromString(T2OLE(szCLSID), &clsidToCompare)) && + ::IsEqualCLSID(clsid, clsidToCompare)) + { + return NS_OK; + } + } while (1); + + return NS_ERROR_FAILURE; +} + +static PRBool +ClassExists(const CLSID &clsid) +{ + // Test if there is a CLSID entry. If there isn't then obviously + // the object doesn't exist. + CRegKey key; + if(key.Open(HKEY_CLASSES_ROOT, _T("CLSID"), KEY_READ) != ERROR_SUCCESS) + return PR_FALSE; // Must fail if we can't even open this! + + LPOLESTR szCLSID = NULL; + if(FAILED(StringFromCLSID(clsid, &szCLSID))) + return PR_FALSE; // Can't allocate string from CLSID + + USES_CONVERSION; + CRegKey keyCLSID; + LONG lResult = keyCLSID.Open(key, W2CT(szCLSID), KEY_READ); + CoTaskMemFree(szCLSID); + if(lResult != ERROR_SUCCESS) + return PR_FALSE; // Class doesn't exist + + return PR_TRUE; +} + +static PRBool +ClassImplementsCategory(const CLSID &clsid, const CATID &catid, PRBool &bClassExists) +{ + bClassExists = ClassExists(clsid); + // Non existent classes won't implement any category... + if(!bClassExists) + return PR_FALSE; + + // CLSID exists, so try checking what categories it implements + bClassExists = TRUE; + CComPtr catInfo; + HRESULT hr = CoCreateInstance(CLSID_StdComponentCategoriesMgr, NULL, + CLSCTX_INPROC_SERVER, __uuidof(ICatInformation), (LPVOID*) &catInfo); + if(catInfo == NULL) + return PR_FALSE; // Must fail if we can't open the category manager + + // See what categories the class implements + CComPtr enumCATID; + if(FAILED(catInfo->EnumImplCategoriesOfClass(clsid, &enumCATID))) + return PR_FALSE; // Can't enumerate classes in category so fail + + // Search for matching categories + BOOL bFound = FALSE; + CATID catidNext = GUID_NULL; + while (enumCATID->Next(1, &catidNext, NULL) == S_OK) + { + if(::IsEqualCATID(catid, catidNext)) + return PR_TRUE; // Match + } + return PR_FALSE; +} + nsDispatchSupport* nsDispatchSupport::mInstance = nsnull; NS_IMPL_THREADSAFE_ISUPPORTS1(nsDispatchSupport, nsIDispatchSupport) nsDispatchSupport::nsDispatchSupport() { - NS_INIT_ISUPPORTS(); - /* member initializers and constructor code */ + NS_INIT_ISUPPORTS(); + /* member initializers and constructor code */ } nsDispatchSupport::~nsDispatchSupport() { - /* destructor code */ + /* destructor code */ } /** @@ -86,6 +182,154 @@ NS_IMETHODIMP nsDispatchSupport::JSVal2COMVariant(jsval val, VARIANT * comvar) return retval; } +/* boolean isClassSafeToHost (in nsCIDRef clsid, out boolean classExists); */ +NS_IMETHODIMP nsDispatchSupport::IsClassSafeToHost(const nsCID & cid, PRBool *classExists, PRBool *_retval) +{ + NS_ENSURE_ARG_POINTER(_retval); + NS_ENSURE_ARG_POINTER(classExists); + + *_retval = PR_FALSE; + + CLSID clsid = XPCDispnsCID2CLSID(cid); + + *classExists = ClassExists(clsid); + + // Test the Internet Explorer black list + const TCHAR kIEControlsBlacklist[] = _T("SOFTWARE\\Microsoft\\Internet Explorer\\ActiveX Compatibility"); + CRegKey keyExplorer; + if(keyExplorer.Open(HKEY_LOCAL_MACHINE, + kIEControlsBlacklist, KEY_READ) == ERROR_SUCCESS) + { + LPOLESTR szCLSID = NULL; + ::StringFromCLSID(clsid, &szCLSID); + if(szCLSID) + { + CRegKey keyCLSID; + USES_CONVERSION; + if(keyCLSID.Open(keyExplorer, W2T(szCLSID), KEY_READ) == ERROR_SUCCESS) + { + DWORD dwType = REG_DWORD; + DWORD dwFlags = 0; + DWORD dwBufSize = sizeof(dwFlags); + if(::RegQueryValueEx(keyCLSID, _T("Compatibility Flags"), + NULL, &dwType, (LPBYTE) &dwFlags, &dwBufSize) == ERROR_SUCCESS) + { + // Documented flags for this reg key + const DWORD kKillBit = 0x00000400; // MS Knowledge Base 240797 + if(dwFlags & kKillBit) + { + ::CoTaskMemFree(szCLSID); + *_retval = PR_FALSE; + return NS_OK; + } + } + } + ::CoTaskMemFree(szCLSID); + } + } + + // Registry keys containing lists of controls that the Gecko explicitly does + // or does not support. + + const TCHAR kControlsToDenyKey[] = _T("Software\\Mozilla\\ActiveX\\Blacklist\\CLSID"); + const TCHAR kControlsToAllowKey[] = _T("Software\\Mozilla\\ActiveX\\Whitelist\\CLSID"); + + // Check if the CLSID belongs to a list that the Gecko does not support + + PRBool listIsEmpty = PR_FALSE; + if(NS_SUCCEEDED(ClassIsListed(HKEY_LOCAL_MACHINE, kControlsToDenyKey, clsid, listIsEmpty))) + { + *_retval = PR_FALSE; + return NS_OK; + } + + // Check if the CLSID is in the whitelist. This test only cares when the whitelist is + // non-empty, to indicates that it is being used. + + listIsEmpty = PR_FALSE; + if(NS_FAILED(ClassIsListed(HKEY_LOCAL_MACHINE, kControlsToAllowKey, clsid, listIsEmpty)) && + !listIsEmpty) + { + *_retval = PR_FALSE; + return NS_OK; + } + + *_retval = PR_TRUE; + return NS_OK; +} + +/* boolean isClassMarkedSafeForScripting (in nsCIDRef clsid, out boolean classExists); */ +NS_IMETHODIMP nsDispatchSupport::IsClassMarkedSafeForScripting(const nsCID & cid, PRBool *classExists, PRBool *_retval) +{ + NS_ENSURE_ARG_POINTER(_retval); + NS_ENSURE_ARG_POINTER(classExists); + // Test the category the object belongs to + CLSID clsid = XPCDispnsCID2CLSID(cid); + *_retval = ClassImplementsCategory(clsid, CATID_SafeForScripting, *classExists); + return NS_OK; +} + +/* boolean isObjectSafeForScripting (in voidPtr theObject, in nsIIDRef iid); */ +NS_IMETHODIMP nsDispatchSupport::IsObjectSafeForScripting(void * theObject, const nsIID & id, PRBool *_retval) +{ + NS_ENSURE_ARG_POINTER(theObject); + NS_ENSURE_ARG_POINTER(_retval); + + // Test if the object implements IObjectSafety and is marked safe for scripting + IUnknown *pObject = (IUnknown *) theObject; + IID iid = XPCDispIID2IID(id); + + // Ask the control if its safe for scripting + CComQIPtr objectSafety = pObject; + if(!objectSafety) + { + *_retval = PR_FALSE; + return NS_OK; + } + + DWORD dwSupported = 0; // Supported options (mask) + DWORD dwEnabled = 0; // Enabled options + + // Assume scripting via IDispatch + if(FAILED(objectSafety->GetInterfaceSafetyOptions( + iid, &dwSupported, &dwEnabled))) + { + // Interface is not safe or failure. + *_retval = PR_FALSE; + return NS_OK; + } + + // Test if safe for scripting + if(!(dwEnabled & dwSupported) & INTERFACESAFE_FOR_UNTRUSTED_CALLER) + { + *_retval = PR_FALSE; + return NS_OK; + } + + *_retval = PR_TRUE; + return NS_OK; +} + +static const PRUint32 kDefaultHostingFlags = + nsIActiveXSecurityPolicy::HOSTING_FLAGS_HOST_NOTHING; + +/* unsigned long getHostingFlags (in string aContext); */ +NS_IMETHODIMP nsDispatchSupport::GetHostingFlags(const char *aContext, PRUint32 *_retval) +{ + NS_ENSURE_ARG_POINTER(_retval); + + // Ask the activex security policy what the hosting flags are + nsresult rv; + nsCOMPtr securityPolicy = + do_GetService(NS_IACTIVEXSECURITYPOLICY_CONTRACTID, &rv); + if(NS_SUCCEEDED(rv) && securityPolicy) + return securityPolicy->GetHostingFlags(aContext, _retval); + + // No policy so use the defaults + *_retval = kDefaultHostingFlags; + return NS_OK; +} + /** * Creates an instance of an COM object, returning it as an IDispatch interface. * This also allows testing of scriptability. @@ -97,14 +341,13 @@ NS_IMETHODIMP nsDispatchSupport::JSVal2COMVariant(jsval val, VARIANT * comvar) * @return nsresult */ NS_IMETHODIMP nsDispatchSupport::CreateInstance(const nsAString & className, - PRBool testScriptability, IDispatch ** result) { if (!nsXPConnect::IsIDispatchEnabled()) return NS_ERROR_XPC_IDISPATCH_NOT_ENABLED; const nsPromiseFlatString & flat = PromiseFlatString(className); CComBSTR name(flat.Length(), flat.get()); - return XPCDispObject::COMCreateInstance(name, testScriptability, result); + return XPCDispObject::COMCreateInstance(name, PR_TRUE, result); } nsDispatchSupport* nsDispatchSupport::GetSingleton()