From 686ea7cd35b96602078bdf52459089274ac5331d Mon Sep 17 00:00:00 2001 From: Michael Curran Date: Mon, 16 Apr 2012 11:24:15 +1000 Subject: [PATCH] A bit of a re-write to nvdahelper injection and rpc code to allow NVDA access to inproc rpc interfaces such as virtualBuffers hosted in an appContainer process (e.g. metro app). Assuming NVDA has the UIAccess privilidge, virtualBuffers are now available in metro apps such as Store. Specific changes: * On win8 nvdaHelperLocal registers the nvdaController and nvdaControllerInternal interfaces using the new RpcServerRegisterIf3, allowing the use of a security descriptor which specifically allows access from AppContainer integrity processes. However as the security descriptor overrides any defaults, it also for now allows access from any authenticated users -- this could be tightened up. * nvdaInprocUtils now has two new methods: registerNVDAProcess and unregisterNVDAProcess, plus an rundown function for the context handle registerNVDAProcess provides. When injection is all initialized, NVDA can call registerNVDAprocess and when it wants the inproc code to terminate it can call unregisterNVDAProcess, or just break the connection or exit. registerNVDAProcess sets an nvdaUnregisteredEvent to nonSignaled, which the inproc code uses to wait on. UnregisterNVDAProcess (also called by the rundown if the connection breaks) sets this event to signaled to instruct the inproc code to terminate. * nvdaHelperRemote: remove the injectionDoneEvent and any code that waited on it or the NVDA process. Its no longer needed. Note that this did not work in AppContainer processes anyway unless a specific security descriptor was set for both the NVDA process and the injectionDoneEvent. * nvdaHelperRemote: When registering its RPC interfaces (nvdaInprocUtils, displayModel and VBuf), use RpcUseProtSeq rather than RpcUseProtSeqEp so as to listen on a dynamic endpoint rather than a well-known one. AppContainer processes mangled the endpoint name anyway. Publish correct dynamic endpoints for each interface along with a generated UUID specific to this instance of the inproc code, using RpcEpRegister, so that NVDA can locate this processes endpoint by using the interface and generated UUID. Finally also call a new nvdaControllerInternal method: requestRegistration to ask NVDA to register with this inproc code before the call returns. * nvdaHelperLocal: rename createConnection to createRemoteBindingHandle (which is more true) and also set a security descriptor on the created binding handle to allow an AppContainer server to talk back to this client -- could be tightened up. createRemoteBindingHandle also now takes a uuid parameter which it uses when creating the binding handle so its able to locate the correct endpoint. * nvdaControllerInternal: add a new requestRegistration method, which takes a UUID string which can be used to locate the caller's own inproc rpc interfaces. The implementation of requestRegistration uses the UUID to create a bindingHandle for remote access to the inproc code using createRemoteBindingHandle, storing it on the appModule which it creates if necessary. It also calls registerNVDAProcess so as to retreave a context handle from the inproc code that if broken will force the inproc code to terminate. This is also stored on the appModule. * AppModule.terminate: If a registrationhandle exists, destroy it with rpcSsDestroyClientContext. If a bindingHandle exists then free it with RpcBindingFree. Destroying both of these is enough to ensure the connection with the inproc code breaks and therefore the rundown for the registration will run and inproc code will be terminated. This is safer than specifically calling unregisterNVDAProcess which could freee or may fail if the remote process is busy or shutting down or is dead etc. * core.initialize: initialize appModuleHandler before nvdaHelper as nvdahelper.nvdaControllerInternal_requestRegistration, which can happen quite quick, needs the ability to create an appModule. --- .../nvdaControllerInternal.acf | 2 +- .../nvdaControllerInternal.idl | 6 +- .../interfaces/nvdaInProcUtils/nvdaInProcUtils.acf | 3 + .../interfaces/nvdaInProcUtils/nvdaInProcUtils.idl | 5 + nvdaHelper/local/nvdaControllerInternal.c | 6 +- nvdaHelper/local/nvdaHelperLocal.cpp | 41 ++++-- nvdaHelper/local/nvdaHelperLocal.def | 6 +- nvdaHelper/local/nvdaHelperLocal.h | 3 +- nvdaHelper/local/rpcSrv.cpp | 36 +++++- nvdaHelper/local/sconscript | 1 + nvdaHelper/remote/inProcess.cpp | 3 - nvdaHelper/remote/injection.cpp | 144 +++++++++------------ nvdaHelper/remote/rpcSrv.cpp | 65 +++++++++- nvdaHelper/remote/rpcSrv.h | 6 +- source/NVDAHelper.py | 33 ++++- source/appModuleHandler.py | 8 +- source/core.py | 6 +- 17 files changed, 243 insertions(+), 131 deletions(-) diff --git a/nvdaHelper/interfaces/nvdaControllerInternal/nvdaControllerInternal.acf b/nvdaHelper/interfaces/nvdaControllerInternal/nvdaControllerInternal.acf index 15fa72f79..d1187d711 100644 --- a/nvdaHelper/interfaces/nvdaControllerInternal/nvdaControllerInternal.acf +++ b/nvdaHelper/interfaces/nvdaControllerInternal/nvdaControllerInternal.acf @@ -16,7 +16,7 @@ http://www.gnu.org/licenses/old-licenses/gpl-2.0.html implicit_handle(handle_t nvdaControllerInternalBindingHandle) ] interface NvdaControllerInternal { - [fault_status,comm_status] getNVDAProcessID(); + [fault_status,comm_status] requestRegistration(); [fault_status,comm_status] inputLangChangeNotify(); [fault_status,comm_status] typedCharacterNotify(); [fault_status,comm_status] displayModelTextChangeNotify(); diff --git a/nvdaHelper/interfaces/nvdaControllerInternal/nvdaControllerInternal.idl b/nvdaHelper/interfaces/nvdaControllerInternal/nvdaControllerInternal.idl index 06bec7766..462867563 100644 --- a/nvdaHelper/interfaces/nvdaControllerInternal/nvdaControllerInternal.idl +++ b/nvdaHelper/interfaces/nvdaControllerInternal/nvdaControllerInternal.idl @@ -35,11 +35,7 @@ cpp_quote("*/") ] interface NvdaControllerInternal { -/** - * Gets the process ID of NVDA. - * @param pProcessID memory where the retreaved process ID can be placed. - */ - error_status_t __stdcall getNVDAProcessID([out] long* pProcessID); + error_status_t __stdcall requestRegistration([in,string] const wchar_t* uuidString); /** * Notifies NVDA that the keyboard layout has changed for this thread. diff --git a/nvdaHelper/interfaces/nvdaInProcUtils/nvdaInProcUtils.acf b/nvdaHelper/interfaces/nvdaInProcUtils/nvdaInProcUtils.acf index ba2ba931e..4cacb9b5d 100644 --- a/nvdaHelper/interfaces/nvdaInProcUtils/nvdaInProcUtils.acf +++ b/nvdaHelper/interfaces/nvdaInProcUtils/nvdaInProcUtils.acf @@ -12,7 +12,10 @@ This license can be found at: http://www.gnu.org/licenses/old-licenses/gpl-2.0.html */ +[strict_context_handle] interface NvdaInProcUtils { + [fault_status,comm_status] registerNVDAProcess(); + [fault_status,comm_status] unregisterNVDAProcess(); [fault_status,comm_status] winword_expandToLine(); [fault_status,comm_status] winword_getTextInRange(); } diff --git a/nvdaHelper/interfaces/nvdaInProcUtils/nvdaInProcUtils.idl b/nvdaHelper/interfaces/nvdaInProcUtils/nvdaInProcUtils.idl index 8ccddb9b4..045c240e9 100644 --- a/nvdaHelper/interfaces/nvdaInProcUtils/nvdaInProcUtils.idl +++ b/nvdaHelper/interfaces/nvdaInProcUtils/nvdaInProcUtils.idl @@ -38,6 +38,11 @@ import "oaidl.idl"; ] interface NvdaInProcUtils { + typedef [context_handle] void* nvdaRegistrationHandle_t; + + error_status_t registerNVDAProcess([in] handle_t bindingHandle, [out] nvdaRegistrationHandle_t* registrationhandle); + error_status_t unregisterNVDAProcess([in,out] nvdaRegistrationHandle_t* registrationhandle); + error_status_t winword_expandToLine([in] const long windowHandle, [in] const int offset, [out] int* lineStart, [out] int* lineEnd); error_status_t winword_getTextInRange([in] const long windowHandle, [in] const int startOffset, [in] const int endOffset, [in] const long formatConfig, [out] BSTR* text); diff --git a/nvdaHelper/local/nvdaControllerInternal.c b/nvdaHelper/local/nvdaControllerInternal.c index f84aee1f7..5f9a85bf4 100644 --- a/nvdaHelper/local/nvdaControllerInternal.c +++ b/nvdaHelper/local/nvdaControllerInternal.c @@ -14,9 +14,9 @@ http://www.gnu.org/licenses/old-licenses/gpl-2.0.html #include "nvdaControllerInternal.h" -error_status_t __stdcall nvdaControllerInternal_getNVDAProcessID(long* pProcessID) { - *pProcessID=GetCurrentProcessId(); - return RPC_S_OK; +error_status_t(__stdcall *_nvdaControllerInternal_requestRegistration)(const wchar_t*); +error_status_t __stdcall nvdaControllerInternal_requestRegistration(const wchar_t* uuidString) { + return _nvdaControllerInternal_requestRegistration(uuidString); } error_status_t(__stdcall *_nvdaControllerInternal_inputLangChangeNotify)(const long, const unsigned long, const wchar_t*); diff --git a/nvdaHelper/local/nvdaHelperLocal.cpp b/nvdaHelper/local/nvdaHelperLocal.cpp index 12a0ab73e..31f827051 100644 --- a/nvdaHelper/local/nvdaHelperLocal.cpp +++ b/nvdaHelper/local/nvdaHelperLocal.cpp @@ -15,6 +15,8 @@ http://www.gnu.org/licenses/old-licenses/gpl-2.0.html #include #include #include +#include +#include #include "nvdaHelperLocal.h" #include "dllImportTableHooks.h" #include "rpcsrv.h" @@ -22,22 +24,45 @@ http://www.gnu.org/licenses/old-licenses/gpl-2.0.html DllImportTableHooks* oleaccHooks = NULL; DllImportTableHooks* uiaCoreHooks = NULL; -handle_t createConnection(int processID) { +typedef struct _RPC_SECURITY_QOS_V5_W { + unsigned long Version; + unsigned long Capabilities; + unsigned long IdentityTracking; + unsigned long ImpersonationType; + unsigned long AdditionalSecurityInfoType; + union + { + RPC_HTTP_TRANSPORT_CREDENTIALS_W *HttpCredentials; + } u; + void *Sid; + unsigned int EffectiveOnly; + void *ServerSecurityDescriptor; +} RPC_SECURITY_QOS_V5_W, *PRPC_SECURITY_QOS_V5_W; + +handle_t createRemoteBindingHandle(wchar_t* uuidString) { RPC_STATUS rpcStatus; - std::wostringstream addr; - addr< -handle_t createConnection(int processID); -void destroyConnection(handle_t bindingHandle); +handle_t createRemoteBindingHandle(wchar_t* uuidString); LRESULT cancellableSendMessageTimeout(HWND hwnd, UINT Msg, WPARAM wParam, LPARAM lParam, UINT fuFlags, UINT uTimeout, PDWORD_PTR lpdwResult); void cancelSendMessage(); void nvdaHelperLocal_initialize(); diff --git a/nvdaHelper/local/rpcSrv.cpp b/nvdaHelper/local/rpcSrv.cpp index 9b9ee0427..ef8ae15ee 100644 --- a/nvdaHelper/local/rpcSrv.cpp +++ b/nvdaHelper/local/rpcSrv.cpp @@ -15,6 +15,7 @@ http://www.gnu.org/licenses/old-licenses/gpl-2.0.html #include #include #include +#include #include "nvdaController.h" #include "nvdaControllerInternal.h" #include @@ -22,6 +23,8 @@ http://www.gnu.org/licenses/old-licenses/gpl-2.0.html using namespace std; +typedef RPC_STATUS(RPC_ENTRY *RpcServerRegisterIf3_functype)(RPC_IF_HANDLE,UUID __RPC_FAR*,RPC_MGR_EPV __RPC_FAR*,unsigned int,unsigned int,unsigned int,RPC_IF_CALLBACK_FN __RPC_FAR*,void __RPC_FAR*); + RPC_IF_HANDLE availableInterfaces[]={ nvdaController_NvdaController_v1_0_s_ifspec, nvdaControllerInternal_NvdaControllerInternal_v1_0_s_ifspec @@ -40,22 +43,41 @@ void __RPC_USER midl_user_free(void* p) { RPC_STATUS startServer() { RPC_STATUS status; - //Set the protocol wchar_t desktopSpecificNamespace[64]; generateDesktopSpecificNamespace(desktopSpecificNamespace,ARRAYSIZE(desktopSpecificNamespace)); - wstringstream endpointStringStream; - endpointStringStream< #define WIN32_LEAN_AND_MEAN #include -#include "rpcSrv.h" #include "winword.h" #include "inputLangChange.h" #include "typedCharacter.h" @@ -45,11 +44,9 @@ void inProcess_initialize() { inputLangChange_inProcess_initialize(); winword_inProcess_initialize(); gdiHooks_inProcess_initialize(); - rpcSrv_inProcess_initialize(); } void inProcess_terminate() { - rpcSrv_inProcess_terminate(); gdiHooks_inProcess_terminate(); winword_inProcess_terminate(); inputLangChange_inProcess_terminate(); diff --git a/nvdaHelper/remote/injection.cpp b/nvdaHelper/remote/injection.cpp index cc3442ab1..78026bb84 100644 --- a/nvdaHelper/remote/injection.cpp +++ b/nvdaHelper/remote/injection.cpp @@ -18,6 +18,7 @@ http://www.gnu.org/licenses/old-licenses/gpl-2.0.html #define WIN32_LEAN_AND_MEAN #include #include +#include #include "log.h" #include "ia2Support.h" #include "apiHook.h" @@ -28,6 +29,7 @@ http://www.gnu.org/licenses/old-licenses/gpl-2.0.html #include "dllmain.h" #include "nvdaHelperRemote.h" #include "inProcess.h" +#include "rpcSrv.h" using namespace std; @@ -101,79 +103,63 @@ DWORD WINAPI inprocMgrThreadFunc(LPVOID data) { HINSTANCE tempHandle=NULL; if(!GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS,(LPCTSTR)dllHandle,&tempHandle)) { LOG_ERROR(L"GetModuleHandleEx failed, GetLastError returned "<0) { - waitHandles[0]=OpenProcess(SYNCHRONIZE,FALSE,nvdaProcessID); - wstringstream s; - s< #include #include +#include #include "nvdaControllerInternal.h" #include "log.h" #include "vbufRemote.h" #include "displayModelRemote.h" -#include "nvdaInProcUtils.h" +#include "NvdaInProcUtils.h" +#include "nvdaControllerInternal.h" #include "rpcSrv.h" +typedef RPC_STATUS(RPC_ENTRY *RpcServerRegisterIf3_functype)(RPC_IF_HANDLE,UUID __RPC_FAR*,RPC_MGR_EPV __RPC_FAR*,unsigned int,unsigned int,unsigned int,RPC_IF_CALLBACK_FN __RPC_FAR*,void __RPC_FAR*); + RPC_IF_HANDLE availableInterfaces[]={ nvdaInProcUtils_NvdaInProcUtils_v1_0_s_ifspec, displayModelRemote_DisplayModel_v1_0_s_ifspec, @@ -38,28 +42,75 @@ void __RPC_USER midl_user_free(void* p) { free(p); } -void rpcSrv_inProcess_initialize() { +HANDLE nvdaUnregisteredEvent=NULL; +RPC_BINDING_VECTOR* bindingVector; +UUID nvdaInprocUuid; + +RPC_STATUS rpcSrv_initialize() { + nvdaUnregisteredEvent=CreateEvent(NULL,TRUE,false,NULL); RPC_STATUS status; //Set the protocol - std::wostringstream endPoint; - endPoint<"%(self.appModuleName,self.appName,self.processID,id(self)) @@ -219,7 +220,10 @@ class AppModule(baseObject.ScriptableObject): Subclasses should call the superclass method first. """ winKernel.closeHandle(self.processHandle) - NVDAHelper.localLib.destroyConnection(self.helperLocalBindingHandle) + if self._inprocRegistrationHandle: + ctypes.windll.rpcrt4.RpcSsDestroyClientContext(ctypes.byref(self._inprocRegistrationHandle)) + if self.helperLocalBindingHandle: + ctypes.windll.rpcrt4.RpcBindingFree(ctypes.byref(self.helperLocalBindingHandle)) def chooseNVDAObjectOverlayClasses(self, obj, clsList): """Choose NVDAObject overlay classes for a given NVDAObject. diff --git a/source/core.py b/source/core.py index 7c428ae18..e271d42df 100644 --- a/source/core.py +++ b/source/core.py @@ -172,6 +172,9 @@ This initializes all modules such as audio, IAccessible, keyboard, mouse, and GU log.info("Using Python version %s"%sys.version) log.info("Using comtypes version %s"%comtypes.__version__) log.debug("Creating wx application instance") + import appModuleHandler + log.debug("Initializing appModule Handler") + appModuleHandler.initialize() import NVDAHelper log.debug("Initializing NVDAHelper") NVDAHelper.initialize() @@ -226,9 +229,6 @@ This initializes all modules such as audio, IAccessible, keyboard, mouse, and GU locale.Init(lang,wxLang) except: pass - import appModuleHandler - log.debug("Initializing appModule Handler") - appModuleHandler.initialize() import api import winUser import NVDAObjects.window -- 2.11.0