/******************************************************************** Copyright (c) 1999 Microsoft Corporation Module Name: Slave.cpp Abstract: File for Implementation of CPCHMasterSlave and CPCHUserProcess classes, used to establish a connection between the service and a slave process. Revision History: Davide Massarenti created 03/28/2000 ********************************************************************/ #include "stdafx.h" #include ///////////////////////////////////////////////////////////////////////////// static const WCHAR c_HelpHost[] = HC_ROOT_HELPSVC_BINARIES L"\\HelpHost.exe"; static const DWORD c_Timeout = 100 * 1000; ///////////////////////////////////////////////////////////////////////////// CPCHUserProcess::UserEntry::UserEntry() { // CComBSTR m_bstrUser m_dwSessionID = 0; // DWORD m_dwSessionID; // // CComBSTR m_bstrVendorID; // CComBSTR m_bstrPublicKey; // // GUID m_guid; // CComPtr m_spConnection; m_hToken = NULL; // HANDLE m_hToken; m_hProcess = NULL; // HANDLE m_hProcess; m_phEvent = NULL; // HANDLE* m_phEvent; ::ZeroMemory( &m_guid, sizeof(m_guid) ); } CPCHUserProcess::UserEntry::~UserEntry() { Cleanup(); } void CPCHUserProcess::UserEntry::Cleanup() { m_bstrUser .Empty (); m_dwSessionID = 0; m_bstrVendorID .Empty (); m_bstrPublicKey.Empty (); m_spConnection .Release(); if(m_hProcess) { ::CloseHandle( m_hProcess ); m_hProcess = NULL; } if(m_hToken) { ::CloseHandle( m_hToken ); m_hToken = NULL; } } //////////////////// bool CPCHUserProcess::UserEntry::operator==( /*[in]*/ const UserEntry& ue ) const { if(ue.m_bstrUser) { if(m_bstrUser == ue.m_bstrUser && m_dwSessionID == ue.m_dwSessionID) return true; } if(ue.m_bstrVendorID) { if(MPC::StrICmp( m_bstrVendorID, ue.m_bstrVendorID ) == 0) return true; } return false; } bool CPCHUserProcess::UserEntry::operator==( /*[in]*/ const GUID& guid ) const { return ::IsEqualGUID( m_guid, guid ) ? true : false; } //////////////////// HRESULT CPCHUserProcess::UserEntry::Clone( /*[in]*/ const UserEntry& ue ) { __HCP_FUNC_ENTRY( "CPCHUserProcess::UserEntry::Clone" ); HRESULT hr; Cleanup(); m_bstrUser = ue.m_bstrUser; // CComBSTR m_bstrUser; m_dwSessionID = ue.m_dwSessionID; // DWORD m_dwSessionID; // m_bstrVendorID = ue.m_bstrVendorID; // CComBSTR m_bstrVendorID; m_bstrPublicKey = ue.m_bstrPublicKey; // CComBSTR m_bstrPublicKey; // // GUID m_guid; // Used for establishing the connection. // CComPtr m_spConnection; // Live object. // HANDLE m_hToken; // User token. // HANDLE m_hProcess; // Process handle. // HANDLE* m_phEvent; // To notify activator. if(ue.m_hToken) { __MPC_EXIT_IF_CALL_RETURNS_FALSE(hr, ::DuplicateTokenEx( ue.m_hToken, 0, NULL, SecurityImpersonation, TokenPrimary, &m_hToken )); } hr = S_OK; __HCP_FUNC_CLEANUP; __HCP_FUNC_EXIT(hr); } HRESULT CPCHUserProcess::UserEntry::Connect( /*[out]*/ HANDLE& hEvent ) { __HCP_FUNC_ENTRY( "CPCHUserProcess::UserEntry::Connect" ); HRESULT hr; // // Logon the user, if not already done. // if(m_hToken == NULL) { CPCHAccounts acc; LPCWSTR szUser = SAFEBSTR( m_bstrUser ); //// // DEBUG //// // DEBUG //// // DEBUG //// __MPC_EXIT_IF_CALL_RETURNS_FALSE(hr, ::OpenProcessToken( ::GetCurrentProcess(), TOKEN_ALL_ACCESS, &m_hToken )); // // Only keep the account enabled for the time it takes to create its token. // __MPC_EXIT_IF_METHOD_FAILS(hr, acc.ChangeUserStatus( szUser, /*fEnable*/true )); __MPC_EXIT_IF_METHOD_FAILS(hr, acc.LogonUser ( szUser, NULL, m_hToken )); __MPC_EXIT_IF_METHOD_FAILS(hr, acc.ChangeUserStatus( szUser, /*fEnable*/false )); } if(m_hProcess == NULL || m_spConnection == NULL ) { __MPC_EXIT_IF_METHOD_FAILS(hr, SendActivation( hEvent )); } hr = S_OK; __HCP_FUNC_CLEANUP; __HCP_FUNC_EXIT(hr); } HRESULT CPCHUserProcess::UserEntry::SendActivation( /*[out]*/ HANDLE& hEvent ) { __HCP_FUNC_ENTRY( "CPCHUserProcess::UserEntry::SendActivation" ); HRESULT hr; DWORD dwRes; PROCESS_INFORMATION piProcessInformation; STARTUPINFOW siStartupInfo; VOID* pEnvBlock = NULL; MPC::wstring strExe( c_HelpHost ); MPC::SubstituteEnvVariables( strExe ); WCHAR rgCommandLine[1024]; ::ZeroMemory( (PVOID)&piProcessInformation, sizeof( piProcessInformation ) ); ::ZeroMemory( (PVOID)&siStartupInfo , sizeof( siStartupInfo ) ); siStartupInfo.cb = sizeof( siStartupInfo ); // // Generate random ID. // __MPC_EXIT_IF_METHOD_FAILS(hr, ::CoCreateGuid( &m_guid )); // // Create event. // __MPC_EXIT_IF_CALL_RETURNS_NULL(hr, (hEvent = ::CreateEvent( NULL, FALSE, FALSE, NULL ))); // // Create process as user. // { CComBSTR bstrGUID( m_guid ); swprintf( rgCommandLine, L"\"%s\" -guid %s", strExe.c_str(), (BSTR)bstrGUID ); } __MPC_EXIT_IF_CALL_RETURNS_FALSE(hr, ::CreateEnvironmentBlock( &pEnvBlock, m_hToken, TRUE )); //// // DEBUG //// // DEBUG //// // DEBUG //// __MPC_EXIT_IF_CALL_RETURNS_FALSE(hr, ::CreateProcessW( NULL, //// rgCommandLine, //// NULL, //// NULL, //// FALSE, //// NORMAL_PRIORITY_CLASS, //// NULL, //// NULL, //// &siStartupInfo, //// &piProcessInformation )); // REAL // REAL // REAL __MPC_EXIT_IF_CALL_RETURNS_FALSE(hr, ::CreateProcessAsUserW( m_hToken , NULL , rgCommandLine , NULL , NULL , FALSE , NORMAL_PRIORITY_CLASS | CREATE_UNICODE_ENVIRONMENT , pEnvBlock , NULL , &siStartupInfo , &piProcessInformation )); m_hProcess = piProcessInformation.hProcess; piProcessInformation.hProcess = NULL; hr = S_OK; __HCP_FUNC_CLEANUP; if(pEnvBlock) ::DestroyEnvironmentBlock( pEnvBlock ); if(piProcessInformation.hProcess) ::CloseHandle( piProcessInformation.hProcess ); if(piProcessInformation.hThread ) ::CloseHandle( piProcessInformation.hThread ); __HCP_FUNC_EXIT(hr); } //////////////////// HRESULT CPCHUserProcess::UserEntry::InitializeForVendorAccount( /*[in]*/ BSTR bstrUser , /*[in]*/ BSTR bstrVendorID , /*[in]*/ BSTR bstrPublicKey ) { __HCP_FUNC_ENTRY( "CPCHUserProcess::UserEntry::InitializeForVendorAccount" ); HRESULT hr; Cleanup(); m_bstrUser = bstrUser; m_bstrVendorID = bstrVendorID; m_bstrPublicKey = bstrPublicKey; hr = S_OK; __HCP_FUNC_EXIT(hr); } HRESULT CPCHUserProcess::UserEntry::InitializeForImpersonation( /*[in]*/ HANDLE hToken ) { __HCP_FUNC_ENTRY( "CPCHUserProcess::UserEntry::InitializeForImpersonation" ); HRESULT hr; MPC::Impersonation imp; MPC::wstring strUser; DWORD dwSize; PSID pUserSid = NULL; if(hToken == NULL) { __MPC_EXIT_IF_METHOD_FAILS(hr, imp.Initialize( MAXIMUM_ALLOWED )); hToken = imp; } __MPC_EXIT_IF_CALL_RETURNS_FALSE(hr, ::DuplicateTokenEx( hToken, 0, NULL, SecurityImpersonation, TokenPrimary, &m_hToken )); __MPC_EXIT_IF_CALL_RETURNS_FALSE(hr, ::GetTokenInformation( m_hToken, TokenSessionId, &m_dwSessionID, sizeof(m_dwSessionID), &dwSize )); __MPC_EXIT_IF_METHOD_FAILS(hr, MPC::SecurityDescriptor::GetTokenSids ( m_hToken, &pUserSid, NULL )); __MPC_EXIT_IF_METHOD_FAILS(hr, MPC::SecurityDescriptor::ConvertSIDToPrincipal( pUserSid, strUser )); m_bstrUser = strUser.c_str(); hr = S_OK; __HCP_FUNC_CLEANUP; MPC::SecurityDescriptor::ReleaseMemory( pUserSid ); __HCP_FUNC_EXIT(hr); } ///////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////// CPCHUserProcess::CPCHUserProcess() { // List m_lst; (void)MPC::_MPC_Module.RegisterCallback( this, (void (CPCHUserProcess::*)())Shutdown ); } CPCHUserProcess::~CPCHUserProcess() { MPC::CallDestructorForAll( m_lst ); MPC::_MPC_Module.UnregisterCallback( this ); } //////////////////// CPCHUserProcess* CPCHUserProcess::s_GLOBAL( NULL ); HRESULT CPCHUserProcess::InitializeSystem() { if(s_GLOBAL == NULL) { s_GLOBAL = new CPCHUserProcess; } return s_GLOBAL ? S_OK : E_OUTOFMEMORY; } void CPCHUserProcess::FinalizeSystem() { if(s_GLOBAL) { delete s_GLOBAL; s_GLOBAL = NULL; } } //////////////////////////////////////////////////////////////////////////////// void CPCHUserProcess::Shutdown() { __HCP_FUNC_ENTRY( "CPCHUserProcess::Shutdown" ); MPC::SmartLock<_ThreadModel> lock( this ); m_lst.clear(); } CPCHUserProcess::UserEntry* CPCHUserProcess::Lookup( /*[in]*/ const UserEntry& ue, /*[in]*/ bool fRelease ) { UserEntry* ueReal; // // Locate vendor and connect to it. // for(Iter it = m_lst.begin(); it != m_lst.end(); ) { ueReal = *it; if(ueReal && *ueReal == ue) { if(fRelease) { delete ueReal; m_lst.erase( it++ ); continue; } else { return ueReal; } } it++; } return NULL; } HRESULT CPCHUserProcess::Remove( /*[in]*/ const UserEntry& ue ) { __HCP_FUNC_ENTRY( "CPCHUserProcess::Remove" ); HRESULT hr; MPC::SmartLock<_ThreadModel> lock( this ); (void)Lookup( ue, /*fRelease*/true ); hr = S_OK; __HCP_FUNC_EXIT(hr); } HRESULT CPCHUserProcess::Connect( /*[in ]*/ const UserEntry& ue , /*[out]*/ IPCHSlaveProcess* *spConnection ) { __HCP_FUNC_ENTRY( "CPCHUserProcess::Connect" ); HRESULT hr; MPC::SmartLock<_ThreadModel> lock( this ); HANDLE hEvent = NULL; UserEntry* ueReal = NULL; DEBUG_AppendPerf( DEBUG_PERF_HELPHOST, "CPCHUserProcess::Connect" ); // // Locate vendor and connect to it. // ueReal = Lookup( ue, /*fRelease*/false ); if(ueReal == NULL) { __MPC_EXIT_IF_ALLOC_FAILS(hr, ueReal, new UserEntry); m_lst.push_back( ueReal ); __MPC_EXIT_IF_METHOD_FAILS(hr, ueReal->Clone( ue )); } __MPC_EXIT_IF_METHOD_FAILS(hr, ueReal->Connect( hEvent )); // // If "Connect" returns an event handle, wait on it. // if(hEvent) { ueReal->m_phEvent = &hEvent; // For waiting response... lock = NULL; if(::WaitForSingleObject( hEvent, c_Timeout ) != WAIT_OBJECT_0) { __MPC_SET_ERROR_AND_EXIT(hr, E_FAIL); } lock = this; // // Relocate vendor (we release the lock on the object, so "ueReal" is not valid anymore. // ueReal = Lookup( ue, /*fRelease*/false ); if(ueReal == NULL) { __MPC_SET_WIN32_ERROR_AND_EXIT(hr, ERROR_FILE_NOT_FOUND); } } __MPC_EXIT_IF_METHOD_FAILS(hr, ueReal->m_spConnection.QueryInterface( spConnection )); hr = S_OK; __HCP_FUNC_CLEANUP; if(hEvent) ::CloseHandle( hEvent ); DEBUG_AppendPerf( DEBUG_PERF_HELPHOST, "CPCHUserProcess::Connect - done" ); __HCP_FUNC_EXIT(hr); } HRESULT CPCHUserProcess::RegisterHost( /*[in]*/ BSTR bstrID , /*[in]*/ IPCHSlaveProcess* pObj ) { __HCP_FUNC_ENTRY( "CPCHUserProcess::RegisterHost" ); HRESULT hr; MPC::SmartLock<_ThreadModel> lock( this ); UserEntry* ueReal = NULL; GUID guid; // // Validate input. // __MPC_EXIT_IF_METHOD_FAILS(hr, ::CLSIDFromString( bstrID, &guid )); // // Locate vendor and connect to it. // for(Iter it = m_lst.begin(); it != m_lst.end(); it++) { ueReal = *it; if(ueReal && *ueReal == guid) { break; } } if(ueReal == NULL) { __MPC_SET_WIN32_ERROR_AND_EXIT(hr, ERROR_FILE_NOT_FOUND); } __MPC_EXIT_IF_METHOD_FAILS(hr, pObj->Initialize( ueReal->m_bstrVendorID, ueReal->m_bstrPublicKey )); ueReal->m_spConnection = pObj; hr = S_OK; __HCP_FUNC_CLEANUP; if(ueReal) { if(ueReal->m_phEvent) { // // Signal the event handle, to awake the activator. // ::SetEvent( *(ueReal->m_phEvent) ); ueReal->m_phEvent = NULL; } } __HCP_FUNC_EXIT(hr); } //////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// HRESULT CPCHUserProcess::SendResponse( /*[in]*/ DWORD dwArgc , /*[in]*/ LPCWSTR* lpszArgv ) { __HCP_FUNC_ENTRY( "CPCHUserProcess::SendResponse" ); HRESULT hr; int i; CComBSTR bstrGUID; CComPtr srv; CComPtr obj; #ifdef DEBUG bool fDebug = false; #endif // // Parse the arguments. // for(i=1; iRegisterHost( bstrGUID, obj )); hr = S_OK; __HCP_FUNC_CLEANUP; __HCP_FUNC_EXIT(hr); } ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// CPCHSlaveProcess::CPCHSlaveProcess() { // CComBSTR m_bstrVendorID; // CComBSTR m_bstrPublicKey; m_ScriptLauncher = NULL; // CPCHScriptWrapper_Launcher* m_ScriptLauncher; } CPCHSlaveProcess::~CPCHSlaveProcess() { delete m_ScriptLauncher; } HRESULT CPCHSlaveProcess::Initialize( /*[in]*/ BSTR bstrVendorID, /*[in]*/ BSTR bstrPublicKey ) { m_bstrVendorID = bstrVendorID; m_bstrPublicKey = bstrPublicKey; return S_OK; } HRESULT CPCHSlaveProcess::CreateInstance( /*[in ]*/ REFCLSID rclsid , /*[in ]*/ IUnknown* pUnkOuter , /*[out]*/ IUnknown* *ppvObject ) { HRESULT hr; DEBUG_AppendPerf( DEBUG_PERF_HELPHOST, "CPCHSlaveProcess::CreateInstance" ); hr = ::CoCreateInstance( rclsid, pUnkOuter, CLSCTX_ALL, IID_IUnknown, (void**)ppvObject ); DEBUG_AppendPerf( DEBUG_PERF_HELPHOST, "CPCHSlaveProcess::CreateInstance - done" ); return hr; } HRESULT CPCHSlaveProcess::CreateScriptWrapper( /*[in ]*/ REFCLSID rclsid , /*[in ]*/ BSTR bstrCode , /*[in ]*/ BSTR bstrURL , /*[out]*/ IUnknown* *ppObj ) { __HCP_FUNC_ENTRY( "CPCHSlaveProcess::CreateScriptWrapper" ); HRESULT hr; MPC::SmartLock<_ThreadModel> lock( this ); if(!m_ScriptLauncher) { __MPC_EXIT_IF_ALLOC_FAILS(hr, m_ScriptLauncher, new CPCHScriptWrapper_Launcher); } __MPC_EXIT_IF_METHOD_FAILS(hr, m_ScriptLauncher->CreateScriptWrapper( rclsid, bstrCode, bstrURL, ppObj )); hr = S_OK; __HCP_FUNC_CLEANUP; __HCP_FUNC_EXIT(hr); } HRESULT CPCHSlaveProcess::OpenBlockingStream( /*[in ]*/ BSTR bstrURL , /*[out]*/ IUnknown* *ppvObject ) { __HCP_FUNC_ENTRY( "CPCHSlaveProcess::OpenBlockingStream" ); HRESULT hr; CComPtr stream; DEBUG_AppendPerf( DEBUG_PERF_HELPHOST, "CPCHSlaveProcess::OpenBlockingStream" ); __MPC_EXIT_IF_METHOD_FAILS(hr, URLOpenBlockingStreamW( NULL, bstrURL, &stream, 0, NULL )); __MPC_EXIT_IF_METHOD_FAILS(hr, stream.QueryInterface( ppvObject )); hr = S_OK; __HCP_FUNC_CLEANUP; DEBUG_AppendPerf( DEBUG_PERF_HELPHOST, "CPCHSlaveProcess::OpenBlockingStream - done" ); __HCP_FUNC_EXIT(hr); } HRESULT CPCHSlaveProcess::IsNetworkAlive( /*[out]*/ VARIANT_BOOL* pfRetVal ) { __HCP_FUNC_ENTRY( "CPCHSlaveProcess::IsNetworkAlive" ); HRESULT hr; __MPC_PARAMCHECK_BEGIN(hr) __MPC_PARAMCHECK_POINTER_AND_SET(pfRetVal,VARIANT_FALSE); __MPC_PARAMCHECK_END(); DEBUG_AppendPerf( DEBUG_PERF_HELPHOST, "CPCHSlaveProcess::IsNetworkAlive" ); if(SUCCEEDED(MPC::Connectivity::NetworkAlive( HC_TIMEOUT_CONNECTIONCHECK ))) { *pfRetVal = VARIANT_TRUE; } hr = S_OK; __HCP_FUNC_CLEANUP; DEBUG_AppendPerf( DEBUG_PERF_HELPHOST, "CPCHSlaveProcess::IsNetworkAlive - done" ); __HCP_FUNC_EXIT(hr); } HRESULT CPCHSlaveProcess::IsDestinationReachable( /*[in ]*/ BSTR bstrDestination, /*[out]*/ VARIANT_BOOL *pfRetVal ) { __HCP_FUNC_ENTRY( "CPCHSlaveProcess::IsDestinationReachable" ); HRESULT hr; __MPC_PARAMCHECK_BEGIN(hr) __MPC_PARAMCHECK_STRING_NOT_EMPTY(bstrDestination); __MPC_PARAMCHECK_POINTER_AND_SET(pfRetVal,VARIANT_FALSE); __MPC_PARAMCHECK_END(); DEBUG_AppendPerf( DEBUG_PERF_HELPHOST, "CPCHSlaveProcess::IsDestinationReachable" ); if(SUCCEEDED(MPC::Connectivity::DestinationReachable( bstrDestination, HC_TIMEOUT_CONNECTIONCHECK ))) { *pfRetVal = VARIANT_TRUE; } hr = S_OK; __HCP_FUNC_CLEANUP; DEBUG_AppendPerf( DEBUG_PERF_HELPHOST, "CPCHSlaveProcess::IsDestinationReachable - done" ); __HCP_FUNC_EXIT(hr); }