Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

1042 lines
26 KiB

//+-------------------------------------------------------------------------
//
// 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);
}