|
|
//+-------------------------------------------------------------------------
//
// Microsoft Windows
// Copyright (C) Microsoft Corporation, 1996.
//
// clsid.cxx
//
// Classes for managing CLSID and APPID registry settings.
//
//--------------------------------------------------------------------------
#include "act.hxx"
#include <catalog.h>
#include <ole2com.h>
#include "registry.hxx"
// The domain name to use if no domain name is specified in the RunAs key.
// We use . instead of the local machine name because the local machine name
// does not work if we are on a Domain Controller, . works in all cases.
WCHAR *gpwszLocalMachineDomain = L".";
//+-------------------------------------------------------------------------
//
// LookupClsidData
//
// Loads the registry configuration for the given CLSID. Option can be any
// of the LOAD_* values defined in clsid.hxx, or 0 for no special handling.
//
//--------------------------------------------------------------------------
HRESULT LookupClsidData( IN GUID & Clsid, IN IComClassInfo* pComClassInfo, IN CToken * pToken, IN DWORD Option, IN OUT CClsidData **ppClsidData ) { HRESULT hr;
if ( ! *ppClsidData ) *ppClsidData = new CClsidData( Clsid, pToken, pComClassInfo );
if ( ! *ppClsidData ) return (E_OUTOFMEMORY);
hr = (*ppClsidData)->Load( Option );
if ( hr != S_OK ) { delete *ppClsidData; *ppClsidData = 0; } else { // In case of Treat As
Clsid = *(*ppClsidData)->ClsidGuid(); }
return (hr); }
//+-------------------------------------------------------------------------
//
// LookupAppidData
//
// Loads the registry configuration for the given APPID.
//
//--------------------------------------------------------------------------
HRESULT LookupAppidData( IN GUID & AppidGuid, IN CToken * pToken, OUT CAppidData ** ppAppidData ) { HRESULT hr = S_OK;
WCHAR wszAppid[GUIDSTR_MAX]; wStringFromGUID2( AppidGuid, wszAppid, sizeof(wszAppid) );
*ppAppidData = new CAppidData( wszAppid, pToken );
if ( ! *ppAppidData ) hr = E_OUTOFMEMORY; else hr = (*ppAppidData)->Load( NULL );
return (hr); }
//
// CClsidData
//
CClsidData::CClsidData( IN GUID & Clsid, IN CToken * pToken, IN IComClassInfo* pComClassInfo ) { if ( pToken ) pToken->AddRef();
_pToken = pToken;
_Clsid = Clsid; _pAppid = NULL; _ServerType = SERVERTYPE_NONE; _DllThreadModel = SINGLE_THREADED; _pwszServer = NULL; _pwszServerExecutable = NULL; _pIComCI = pComClassInfo; if (pComClassInfo) { pComClassInfo->AddRef(); pComClassInfo->Lock(); } _pIClassCI = NULL; _pICPI = NULL; _pwszDarwinId = NULL; _pwszDllSurrogate = NULL; _hSaferLevel = NULL; _dwAcceptableCtx = 0; _bIsInprocClass = FALSE; memset(_wszClsid, 0, sizeof(_wszClsid)); }
CClsidData::~CClsidData() { Purge();
if ( _pToken ) _pToken->Release();
_pToken = 0;
if ( _pIComCI != NULL ) { _pIComCI->Unlock(); _pIComCI->Release(); } if ( _pIClassCI != NULL ) _pIClassCI->Release(); if ( _pICPI != NULL ) _pICPI->Release(); }
//-------------------------------------------------------------------------
//
// CClsidData::Load
//
// Forces a load of all registry keys and values for this clsid and its
// associated APPID.
//
//-------------------------------------------------------------------------
HRESULT CClsidData::Load( IN DWORD Option ) { HRESULT hr = E_FAIL; LocalServerType lsType = LocalServerType32; ProcessType pType = ProcessTypeNormal; ThreadingModel tModel = SingleThreaded; DWORD dwAcceptableClsCtx = 0; IComServices *pServices = NULL; IComClassInfo2 *pIComCI2 = NULL; IClassClassicInfo2 *pClassCI2 = NULL;
//
// Catalog cruft
//
hr = InitializeCatalogIfNecessary(); if ( FAILED(hr) ) { goto cleanup; }
if ( _pIComCI == NULL ) { if ( _pToken ) { // Ok, here's the deal.
// First we need to make sure we've exhausted all of the LocalServer
// options on the machine before turning to a remote server option,
// because the classinfo will always say yes to remote server.
hr = gpCatalogSCM->GetClassInfo ( CLSCTX_LOCAL_SERVER, _pToken, _Clsid, IID_IComClassInfo, (void**) &_pIComCI ); if ( _pIComCI == NULL || hr != S_OK ) { // Exhaustive search for localserver failed, or we just want
// the first thing we find. Just do a normal search.
// REVIEW: Will the do the right thing for 32bit CLSID?
// What about Darwin?
hr = gpCatalogSCM->GetClassInfo ( 0, _pToken, _Clsid, IID_IComClassInfo, (void**) &_pIComCI ); } } else { hr = gpCatalog->GetClassInfo ( _Clsid, IID_IComClassInfo, (void**) &_pIComCI ); }
if ( _pIComCI == NULL || hr != S_OK ) { hr = REGDB_E_CLASSNOTREG; goto cleanup; }
_pIComCI->Lock(); }
hr = _pIComCI->QueryInterface(IID_IComClassInfo2, (void **)&pIComCI2); if(SUCCEEDED(hr)) { BOOL bClassEnabled; pIComCI2->IsEnabled(&bClassEnabled); pIComCI2->Release();
if(bClassEnabled == FALSE) { hr = CO_E_CLASS_DISABLED; goto cleanup; } }
// Treat As possible so read clsid
GUID *pguid; hr = _pIComCI->GetConfiguredClsid(&pguid); _Clsid = *pguid; wStringFromGUID2(_Clsid, _wszClsid, sizeof(_wszClsid));
if ( _pIClassCI == NULL ) { hr = _pIComCI->QueryInterface (IID_IClassClassicInfo, (void**) &_pIClassCI ); if ( _pIClassCI == NULL || hr != S_OK ) { hr = REGDB_E_CLASSNOTREG; goto cleanup; } }
if ( _pICPI == NULL ) { hr = _pIClassCI->GetProcess ( IID_IComProcessInfo, (void**) &_pICPI ); if ( hr != S_OK && _pICPI != NULL ) { _pICPI->Release(); _pICPI = NULL; } hr = S_OK; }
Purge();
//
// Load Surrogate command line
hr = _pIClassCI->GetSurrogateCommandLine(&_pwszDllSurrogate); if ( hr != S_OK ) { // GetSurrogateCommandLine can fail for two reasons: 1) out-of-memory;
// or 2) the class is not configured to run as a surrogate. It is
// somewhat difficult to tell at this point which is which. Therefore,
// we don't let a failure here doesn't stop us; instead we check further
// below for the case where we are supposedly a surrogate but don't have
// a surrogate cmd line.
_pwszDllSurrogate = NULL; hr = S_OK; }
//
// Load APPID settings before other non-Darwin CLSID settings.
//
if ( _pICPI != NULL ) { GUID* pGuidProcessId; WCHAR wszAppid[GUIDSTR_MAX];
hr = _pICPI->GetProcessId (&pGuidProcessId); if ( hr != S_OK ) { hr = REGDB_E_CLASSNOTREG; goto cleanup; } wStringFromGUID2( *pGuidProcessId, wszAppid, sizeof(wszAppid) );
_pAppid = new CAppidData( wszAppid, _pToken );
if ( _pAppid == NULL ) { hr = E_OUTOFMEMORY; } else { hr = _pAppid->Load( _pICPI ); }
if ( hr != S_OK ) { goto cleanup; }
hr = _pICPI->GetProcessType (&pType); if ( hr != S_OK ) { pType = ProcessTypeNormal; hr = S_OK; } }
//
// See if we can find a LocalServer
//
hr = _pIClassCI->GetLocalServerType (&lsType); if ( hr != S_OK ) { hr = S_OK; } else if ( (pType != ProcessTypeService) && (pType != ProcessTypeComPlusService) ) { if ( lsType == LocalServerType16 ) { _ServerType = SERVERTYPE_EXE16; } else { _ServerType = SERVERTYPE_EXE32; }
hr = _pIClassCI->GetModulePath(CLSCTX_LOCAL_SERVER, &_pwszServer); if ( hr != S_OK ) { hr = REGDB_E_CLASSNOTREG; goto cleanup; } else { pType = ProcessTypeNormal; } }
hr = _pIClassCI->QueryInterface(IID_IClassClassicInfo2, (void **)&pClassCI2); if (SUCCEEDED(hr)) { hr = pClassCI2->GetServerExecutable(&_pwszServerExecutable); if (FAILED(hr)) { _pwszServerExecutable = NULL; } pClassCI2->Release(); }
//
// Set up process type information
//
// Determine the acceptable context of creation
hr = _pIComCI->GetClassContext((CLSCTX)(CLSCTX_LOCAL_SERVER | CLSCTX_REMOTE_SERVER), (CLSCTX*) &dwAcceptableClsCtx); if ( SUCCEEDED(hr) ) { if ( !dwAcceptableClsCtx ) { if (Option == LOAD_APPID) { _bIsInprocClass = TRUE; } else { hr = REGDB_E_CLASSNOTREG; } } }
if ( FAILED(hr) ) goto cleanup;
// Set the acceptable context of creation
_dwAcceptableCtx = dwAcceptableClsCtx;
switch ( pType ) { case ProcessTypeNormal: break;
case ProcessTypeService: _ServerType = SERVERTYPE_SERVICE; break;
case ProcessTypeComPlusService: _ServerType = SERVERTYPE_COMPLUS_SVC; break;
case ProcessTypeComPlus: //See if services configured, else normal dllhost
if (_pICPI->QueryInterface(IID_IComServices, (void**) &pServices) == S_OK ) { _ServerType = SERVERTYPE_COMPLUS; pServices->Release(); } else _ServerType = SERVERTYPE_DLLHOST;
break;
case ProcessTypeLegacySurrogate: _ServerType = SERVERTYPE_SURROGATE;
hr = _pIClassCI->GetModulePath(CLSCTX_INPROC_SERVER, &_pwszServer); if ( hr != S_OK ) { hr = REGDB_E_CLASSNOTREG; goto cleanup; } break; default: hr = REGDB_E_CLASSNOTREG; goto cleanup; break; }
//
// Other process info
//
if ( S_OK != _pIClassCI->GetThreadingModel (&tModel) ) { hr = REGDB_E_BADTHREADINGMODEL; } else { switch ( tModel ) { case ApartmentThreaded: _DllThreadModel = APT_THREADED; break; case FreeThreaded: _DllThreadModel = FREE_THREADED; break; case SingleThreaded: default: _DllThreadModel = SINGLE_THREADED; break; case BothThreaded: case NeutralThreaded: _DllThreadModel = BOTH_THREADED; break; } }
//
// Safer Level.
//
hr = CalculateSaferLevel();
cleanup:
if ( _ServerType == SERVERTYPE_SURROGATE || _ServerType == SERVERTYPE_COMPLUS || _ServerType == SERVERTYPE_DLLHOST) { if (!_pwszDllSurrogate) { // we're supposed to be a surrogate, but don't have a cmd line for such
if (SUCCEEDED(hr)) hr = E_OUTOFMEMORY; } }
if ( FAILED(hr) ) { Purge();
if ( _pIComCI != NULL ) { _pIComCI->Unlock(); _pIComCI->Release(); _pIComCI = NULL; }
if ( _pIClassCI != NULL ) { _pIClassCI->Release(); _pIClassCI = NULL; }
if ( _pICPI != NULL ) { _pICPI->Release(); _pICPI = NULL; } }
return (hr); }
//-------------------------------------------------------------------------
//
// CClsidData::Purge
//
// Frees and clears all member data except for clsid registry key and
// Darwin ID values.
//
//-------------------------------------------------------------------------
void CClsidData::Purge() { if ( _pAppid ) { delete _pAppid; }
_pAppid = NULL; _ServerType = SERVERTYPE_NONE; _DllThreadModel = SINGLE_THREADED; _pwszServer = NULL; _pwszServerExecutable = NULL; _pwszDarwinId = NULL; _pwszDllSurrogate = NULL;
if (_hSaferLevel) { SaferCloseLevel(_hSaferLevel); _hSaferLevel = NULL; } }
//-------------------------------------------------------------------------
//
// CClsidData::CalculateSaferLevel
//
// Determine (and open) the safer level for this CLSID. The decision
// process is as follows:
//
// - If a SAFER level is configured on the application, use that.
// - Otherwise, if automatic enforcement is ON:
// - If we can get a SAFER level from the file, use that.
// - Otherwise, if there's a default SAFER level, use that.
// - Otherwise, don't use SAFER.
// - Otherwise, don't use SAFER.
//
//-------------------------------------------------------------------------
HRESULT CClsidData::CalculateSaferLevel() { HRESULT hr = S_OK;
//
// One has already been calculated, return it.
//
if (_hSaferLevel) { return S_OK; }
//
// Get the configured safer level...
//
if (_pAppid) { DWORD dwSaferLevel; // GetSaferLevel returns TRUE if we have a configured safer level....
BOOL fSaferConfigured = _pAppid->GetSaferLevel(&dwSaferLevel); if (fSaferConfigured) { if (!SaferCreateLevel(SAFER_SCOPEID_MACHINE, dwSaferLevel, SAFER_LEVEL_OPEN, &_hSaferLevel, NULL)) { _hSaferLevel = NULL; hr = HRESULT_FROM_WIN32(GetLastError()); } else { hr = S_OK; } } }
return hr; }
//
// CAppidData
//
CAppidData::CAppidData( IN WCHAR * pwszAppid, IN CToken * pToken ) { ASSERT( lstrlenW( pwszAppid ) == GUIDSTR_MAX - 1 );
lstrcpyW( _wszAppid, pwszAppid ); BOOL bGuidConv = wGUIDFromString(_wszAppid,&_GuidAppid);
ASSERT(bGuidConv && "AppID is not a well-formed GUID");
if ( pToken ) pToken->AddRef();
_pToken = pToken;
_bActivateAtStorage = FALSE;
_pwszService = 0; _pwszServiceParameters = 0; _pwszRunAsUser = 0; _pwszRunAsDomain = 0; _pLaunchPermission = 0;
_pwszRemoteServerNames = 0; _bComPlusProcess = FALSE;
_pICPI = NULL;
_dwSaferLevel = 0; _fSaferLevelValid = FALSE; }
CAppidData::~CAppidData() { Purge();
if ( _pToken ) _pToken->Release();
if ( _pICPI != NULL ) _pICPI->Release(); }
//-------------------------------------------------------------------------
//
// CAppidData::Load
//
// Reads all named value settings for this APPID.
//
//-------------------------------------------------------------------------
HRESULT CAppidData::Load( IN IComProcessInfo *pICPI ) { HRESULT hr; IComProcessInfo2 *pICPI2 = NULL; ProcessType pType = ProcessTypeNormal; WCHAR* pwszTmpValue = NULL; WCHAR* pwszRunAsDomain = NULL; int len = 0; DWORD dwJunk;
hr = InitializeCatalogIfNecessary(); if ( FAILED(hr) ) { goto cleanup; }
if ( _pICPI == NULL ) { if ( pICPI != NULL ) { _pICPI = pICPI; _pICPI->AddRef(); } else if ( _pToken ) hr = gpCatalogSCM->GetProcessInfo (0, _pToken, _GuidAppid, IID_IComProcessInfo, (void**) &_pICPI ); else hr = gpCatalog->GetProcessInfo ( _GuidAppid, IID_IComProcessInfo, (void**) &_pICPI );
if ( _pICPI == NULL || hr != S_OK ) { hr = REGDB_E_CLASSNOTREG; goto cleanup; } }
//
// ActivateAtStorage
//
hr = _pICPI->GetActivateAtStorage (&_bActivateAtStorage); if ( hr != S_OK ) { _bActivateAtStorage = FALSE; hr = S_OK; }
//
// ProcessType
//
hr = _pICPI->GetProcessType (&pType); if ( hr != S_OK ) { pType = ProcessTypeNormal; hr = S_OK; }
//
// ComPlus?
//
// NOTE!!!!:: This is true for normal dllhost.exe regardless of
// whether they are really complus or not. We won't
// distinguish this in here in AppidData but in
// ClsidData
_bComPlusProcess = pType == ProcessTypeComPlus;
//
// RemoteServerName
//
hr = _pICPI->GetRemoteServerName (&_pwszRemoteServerNames); if ( hr != S_OK ) { _pwszRemoteServerNames = NULL; hr = S_OK; }
//
// LaunchPermision
//
hr = _pICPI->GetLaunchPermission ( (void**) &_pLaunchPermission, &dwJunk ); if ( hr != S_OK ) { _pLaunchPermission = NULL; hr = S_OK; }
//
// LocalService and ServiceParameters
//
hr = _pICPI->GetServiceName (&_pwszService); if ( hr != S_OK ) { _pwszService = NULL; hr = S_OK; } hr = _pICPI->GetServiceParameters (&_pwszServiceParameters); if ( hr != S_OK ) { _pwszServiceParameters = NULL; hr = S_OK; }
//
// RunAs
//
// Note: current implementation of GetRunAsUser is that it
// returns an error if there is no RunAs user for this appid,
// --OR-- it returns S_OK with a valid outparam.
//
hr = _pICPI->GetRunAsUser (&pwszTmpValue); if ( hr != S_OK ) { _pwszRunAsUser = NULL; _pwszRunAsDomain = NULL; hr = S_OK; } else { len = lstrlenW(pwszTmpValue)+1; _pwszRunAsUser = new WCHAR[len]; if (!_pwszRunAsUser) { hr = E_OUTOFMEMORY; goto cleanup; }
lstrcpyW(_pwszRunAsUser, pwszTmpValue);
pwszTmpValue = _pwszRunAsUser;
while ( *pwszTmpValue && *pwszTmpValue != L'\\' ) pwszTmpValue++;
if ( ! *pwszTmpValue ) { // user name, no domain name, use the machine name
_pwszRunAsDomain = gpwszLocalMachineDomain; } else { // domain\user
ASSERT( L'\\' == *pwszTmpValue ); *pwszTmpValue = 0; _pwszRunAsDomain = _pwszRunAsUser; _pwszRunAsUser = pwszTmpValue + 1;
if ( ! *_pwszRunAsUser ) { hr = E_FAIL; goto cleanup; } } }
//
// Safer trust level.
//
hr = _pICPI->QueryInterface(IID_IComProcessInfo2, (void **)&pICPI2); if (SUCCEEDED(hr)) { hr = pICPI2->GetSaferTrustLevel(&_dwSaferLevel); pICPI2->Release(); } // If we couldn't get a configured trust level, make
// sure we remember that.
if (FAILED(hr)) { _fSaferLevelValid = FALSE; hr = S_OK; } else { _fSaferLevelValid = TRUE; }
cleanup: if ( FAILED(hr) ) { Purge(); if ( _pICPI != NULL ) { _pICPI->Release(); _pICPI = NULL; } }
return (hr); }
//-------------------------------------------------------------------------
//
// CAppidData::Purge
//
// Frees and clears all named value settings for this APPID.
//
//-------------------------------------------------------------------------
void CAppidData::Purge() { _pwszService = 0; _pwszServiceParameters = 0; _pLaunchPermission = 0;
// This is too tricky, so it goes. If we've assigned RunAsDomain
// the address of the global local machine name, then the buffer
// we allocated is pointed at by _pwszRunAsUser. Otherwise, it's
// pointed at by _pwszRunAsDomain ('cause if both user and domain
// are in the string, domain is first).
if ( _pwszRunAsUser != NULL ) { if ( _pwszRunAsDomain == gpwszLocalMachineDomain ) { delete [] _pwszRunAsUser; } else { delete [] _pwszRunAsDomain; } } _pwszRunAsUser = 0; _pwszRunAsDomain = 0;
_fSaferLevelValid = FALSE; _dwSaferLevel = 0;
_pwszRemoteServerNames = 0; _bActivateAtStorage = 0; }
BOOL CAppidData::CertifyServer( CProcess * pProcess ) { PSID pRequiredSid = NULL; HANDLE hToken = 0; BOOL bStatus;
if ( _pwszService ) { SC_HANDLE hService; ASSERT(g_hServiceController); hService = OpenService( g_hServiceController, _pwszService, GENERIC_READ );
if ( ! hService ) return (FALSE);
DWORD dwBufNeeded = 0; bStatus = QueryServiceStatusEx(hService, SC_STATUS_PROCESS_INFO, NULL, 0, &dwBufNeeded); ASSERT(!bStatus); if (!bStatus && (GetLastError() == ERROR_INSUFFICIENT_BUFFER)) { BYTE* pBuf = NULL; SafeAllocaAllocate(pBuf, dwBufNeeded); if (pBuf) { bStatus = QueryServiceStatusEx(hService, SC_STATUS_PROCESS_INFO, pBuf, dwBufNeeded, &dwBufNeeded); if (bStatus) { SERVICE_STATUS_PROCESS* pServiceStatus = (SERVICE_STATUS_PROCESS*)pBuf; // Service must not be stopped or stopping, and service's
// pid should match that of the registering process.
if (pServiceStatus->dwCurrentState == SERVICE_STOPPED || pServiceStatus->dwCurrentState == SERVICE_STOP_PENDING || pServiceStatus->dwProcessId != pProcess->GetPID()) { bStatus = FALSE; } } SafeAllocaFree(pBuf); } } else { bStatus = FALSE; } CloseServiceHandle(hService);
return (bStatus); }
if ( ( ! _pwszRunAsUser ) && ( ! IsInteractiveUser() ) ) return (TRUE);
if ( IsInteractiveUser() ) { ULONG ulSessionID = pProcess->GetToken()->GetSessionId(); hToken = GetUserTokenForSession(ulSessionID); } else { hToken = GetRunAsToken( 0, // CLSCTX isn't always available here
_wszAppid, _pwszRunAsDomain, _pwszRunAsUser, FALSE); } if (hToken) { DWORD dwSaferLevel; if (GetSaferLevel(&dwSaferLevel)) { //
// If a safer level is configured, then the person being launched
// must have the same safer restrictions as we expected put on them.
//
SAFER_LEVEL_HANDLE hSaferLevel = NULL; HANDLE hSaferToken = NULL;
bStatus = SaferCreateLevel(SAFER_SCOPEID_MACHINE, dwSaferLevel, SAFER_LEVEL_OPEN, &hSaferLevel, NULL); if (bStatus) { bStatus = SaferComputeTokenFromLevel(hSaferLevel, hToken, &hSaferToken, 0, NULL); SaferCloseLevel(hSaferLevel); }
if (bStatus) { NtClose(hToken); hToken = hSaferToken; } } else bStatus = TRUE;
if (bStatus) { if (S_OK != pProcess->GetToken()->MatchToken(hToken, TRUE)) bStatus = FALSE; } } else bStatus = FALSE;
if ( hToken ) NtClose( hToken );
return (bStatus); }
|