674 lines
16 KiB
C++
674 lines
16 KiB
C++
//
|
|
// Author: DebiM
|
|
// Date: September 1996
|
|
//
|
|
// File: csuser.cxx
|
|
//
|
|
// Maintains a list of class containers per User SID.
|
|
// Looks up this list for every IClassAccess call from OLE32/SCM.
|
|
//
|
|
//
|
|
//---------------------------------------------------------------------
|
|
|
|
#include "cstore.hxx"
|
|
|
|
//
|
|
// Link list pointer for Class Containers Seen
|
|
//
|
|
extern CLASSCONTAINER *gpContainerHead;
|
|
|
|
//
|
|
// Link list pointer for User Profiles Seen
|
|
//
|
|
extern USERPROFILE *gpUserHead;
|
|
|
|
|
|
// Initialzed in InitializeClassStore at startup
|
|
|
|
extern CRITICAL_SECTION ClassStoreBindList;
|
|
|
|
//-------------------------------------------------------------------------
|
|
//
|
|
// OpenUserRegKey
|
|
//
|
|
// Opens a key under a user's HKEY_CLASSES_ROOT registry key. On NT5
|
|
// HKCR is equivalent to HKEY_USERS\{sid string}\Software\Classes.
|
|
//
|
|
// A SID string is used to create
|
|
// the proper registry key name to open.
|
|
//
|
|
//-------------------------------------------------------------------------
|
|
DWORD
|
|
OpenUserRegKey(
|
|
IN PSID pSid,
|
|
IN WCHAR * pwszSubKey,
|
|
OUT HKEY * phKey
|
|
)
|
|
{
|
|
UNICODE_STRING UnicodeString;
|
|
WCHAR * pwszKey;
|
|
DWORD AllocSize;
|
|
NTSTATUS Status;
|
|
|
|
UnicodeString.Length = UnicodeString.MaximumLength = 0;
|
|
UnicodeString.Buffer = 0;
|
|
|
|
Status = RtlConvertSidToUnicodeString(
|
|
&UnicodeString,
|
|
pSid,
|
|
(BOOLEAN)TRUE // Allocate
|
|
);
|
|
|
|
//
|
|
// Don't return a raw NT status code. This is the only possible error
|
|
// condition presuming our sid is valid.
|
|
//
|
|
if ( Status != STATUS_SUCCESS )
|
|
return ERROR_OUTOFMEMORY;
|
|
|
|
//
|
|
// Your friendly reminder, unicode string length is in bytes and doesn't include
|
|
// null terminator, if any.
|
|
// Add byte for '\\' and end null.
|
|
//
|
|
AllocSize = UnicodeString.Length + ((1 + lstrlen(pwszSubKey) + 1) * sizeof(WCHAR));
|
|
pwszKey = (WCHAR *) alloca( AllocSize );
|
|
|
|
if ( pwszKey )
|
|
{
|
|
memcpy( pwszKey, UnicodeString.Buffer, UnicodeString.Length );
|
|
pwszKey[UnicodeString.Length / 2] = L'\\';
|
|
lstrcpyW( &pwszKey[(UnicodeString.Length / 2) + 1], pwszSubKey );
|
|
}
|
|
|
|
RtlFreeUnicodeString( &UnicodeString );
|
|
|
|
if ( ! pwszKey )
|
|
return ERROR_OUTOFMEMORY;
|
|
|
|
Status = RegOpenKeyEx(
|
|
HKEY_USERS,
|
|
pwszKey,
|
|
0,
|
|
KEY_READ,
|
|
phKey );
|
|
|
|
return Status;
|
|
}
|
|
|
|
//
|
|
// GetUserSid
|
|
// ----------
|
|
//
|
|
// Synopsis: return the user SID of the caller.
|
|
//
|
|
// Arguments: &PSID - Where to store the caller's PSID
|
|
//
|
|
// Returns: HRESULT - S_OK if successful
|
|
// E_FAIL otherwise
|
|
//
|
|
SID LocalSystemSid = { SID_REVISION, 1, SECURITY_NT_AUTHORITY, SECURITY_LOCAL_SYSTEM_RID };
|
|
|
|
|
|
HRESULT GetUserSid(PSID *ppUserSid, UINT *pCallType)
|
|
{
|
|
BYTE achBuffer[100];
|
|
PTOKEN_USER pUser = (PTOKEN_USER) &achBuffer;
|
|
PSID pSid;
|
|
DWORD dwBytesRequired;
|
|
BOOL fAllocatedBuffer = FALSE;
|
|
HRESULT hr = S_OK;
|
|
HANDLE hUserToken = NULL;
|
|
BOOL fImpersonated = TRUE;
|
|
|
|
|
|
*pCallType = CS_CALL_USERPROCESS;
|
|
|
|
// Initialize
|
|
*ppUserSid = NULL;
|
|
|
|
// Get caller's token while impersonating
|
|
|
|
if (!OpenThreadToken(GetCurrentThread(),
|
|
TOKEN_DUPLICATE | TOKEN_QUERY,
|
|
TRUE,
|
|
&hUserToken))
|
|
{
|
|
fImpersonated = FALSE;
|
|
if (ERROR_NO_TOKEN != GetLastError())
|
|
return HRESULT_FROM_WIN32(GetLastError());
|
|
|
|
if (!OpenProcessToken(GetCurrentProcess(),
|
|
TOKEN_DUPLICATE | TOKEN_QUERY,
|
|
&hUserToken))
|
|
{
|
|
return HRESULT_FROM_WIN32(GetLastError());
|
|
}
|
|
}
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
if (!GetTokenInformation(
|
|
hUserToken, // Handle
|
|
TokenUser, // TokenInformationClass
|
|
pUser, // TokenInformation
|
|
sizeof(achBuffer), // TokenInformationLength
|
|
&dwBytesRequired // ReturnLength
|
|
))
|
|
{
|
|
|
|
//
|
|
// Need to handle the case of insufficient buffer size.
|
|
//
|
|
|
|
if (sizeof(achBuffer) >= dwBytesRequired)
|
|
{
|
|
hr = HRESULT_FROM_WIN32(GetLastError());
|
|
}
|
|
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
//
|
|
// Allocate space for the user info
|
|
//
|
|
|
|
pUser = (PTOKEN_USER) CoTaskMemAlloc(dwBytesRequired);
|
|
if (pUser == NULL)
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
}
|
|
}
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
fAllocatedBuffer = TRUE;
|
|
|
|
//
|
|
// Read in the UserInfo
|
|
//
|
|
|
|
|
|
if (!GetTokenInformation(
|
|
hUserToken, // Handle
|
|
TokenUser, // TokenInformationClass
|
|
pUser, // TokenInformation
|
|
dwBytesRequired, // TokenInformationLength
|
|
&dwBytesRequired // ReturnLength
|
|
))
|
|
|
|
{
|
|
hr = HRESULT_FROM_WIN32(GetLastError());
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
if (hUserToken)
|
|
{
|
|
CloseHandle( hUserToken );
|
|
hUserToken = NULL;
|
|
}
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
|
|
//
|
|
// Distinguish between
|
|
// a) LOCAL_SYSTEM,
|
|
// b) Impersonated Call from a LOCAL_SYSTEM
|
|
// and c) In_proc call from a user process
|
|
//
|
|
// For case (c) make the SID null.
|
|
//
|
|
|
|
if (EqualSid(pUser->User.Sid, &LocalSystemSid))
|
|
{
|
|
*pCallType = CS_CALL_LOCALSYSTEM;
|
|
}
|
|
else
|
|
{
|
|
if (fImpersonated)
|
|
{
|
|
*pCallType = CS_CALL_IMPERSONATED;
|
|
}
|
|
else
|
|
{
|
|
*pCallType = CS_CALL_USERPROCESS;
|
|
}
|
|
}
|
|
|
|
// Alloc buffer for copy of SID
|
|
|
|
dwBytesRequired = GetLengthSid(pUser->User.Sid);
|
|
*ppUserSid = CoTaskMemAlloc(dwBytesRequired);
|
|
if (*ppUserSid == NULL)
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
}
|
|
else
|
|
{
|
|
// Copy SID
|
|
|
|
if (!CopySid(dwBytesRequired, *ppUserSid, pUser->User.Sid))
|
|
{
|
|
hr = HRESULT_FROM_WIN32(GetLastError());
|
|
CoTaskMemFree(*ppUserSid);
|
|
*ppUserSid = NULL;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (fAllocatedBuffer == TRUE)
|
|
{
|
|
CoTaskMemFree(pUser);
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
#if 0
|
|
//
|
|
// GetDomainClassStore
|
|
// -------------------
|
|
//
|
|
// This will go away.
|
|
//
|
|
// Currently this is used to get the Class Store Path
|
|
// for the domain.
|
|
//
|
|
|
|
|
|
#define DEFAULTSTORENAME L"CN=ClassStore"
|
|
|
|
|
|
HRESULT
|
|
GetDomainClassStore(
|
|
LPOLESTR * pszDefaultContainer,
|
|
LPOLESTR * pszDefaultStore)
|
|
|
|
//
|
|
// Finds the Root Path for the DC for this machine
|
|
// Then gets the Default Known CLass Store for the DC
|
|
//
|
|
{
|
|
HRESULT hr;
|
|
LPOLESTR PathNames[2];
|
|
VARIANT * pVarFilter = NULL;
|
|
IEnumVARIANT * pEnum;
|
|
IADs * pADs;
|
|
VARIANT VariantArray[2];
|
|
IDispatch * pDispatch = NULL;
|
|
ULONG cFetched;
|
|
IADsContainer * pContainer = NULL;
|
|
|
|
//
|
|
// Do a bind to the DC by a GetObject for the Path LDAP:
|
|
//
|
|
hr = ADsGetObject(
|
|
L"LDAP:",
|
|
IID_IADsContainer,
|
|
(void **)&pContainer
|
|
);
|
|
|
|
RETURN_ON_FAILURE(hr);
|
|
|
|
hr = ADsBuildEnumerator(
|
|
pContainer,
|
|
&pEnum
|
|
);
|
|
|
|
hr = ADsEnumerateNext(
|
|
pEnum,
|
|
1,
|
|
VariantArray,
|
|
&cFetched
|
|
);
|
|
|
|
pEnum->Release();
|
|
|
|
if ((hr == S_FALSE) || (cFetched == 0))
|
|
{
|
|
return E_FAIL;
|
|
}
|
|
|
|
pDispatch = VariantArray[0].pdispVal;
|
|
memset(VariantArray, 0, sizeof(VARIANT)*2);
|
|
hr = pDispatch->QueryInterface(IID_IADs, (void **) &pADs) ;
|
|
pDispatch->Release();
|
|
|
|
pADs->get_ADsPath(pszDefaultContainer);
|
|
pADs->Release();
|
|
pContainer->Release();
|
|
|
|
*pszDefaultStore = DEFAULTSTORENAME;
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
#endif
|
|
|
|
PCLASSCONTAINER
|
|
GetClassStore (LPOLESTR pszPath)
|
|
{
|
|
|
|
PCLASSCONTAINER pCS = NULL;
|
|
|
|
pCS = (CLASSCONTAINER *) CoTaskMemAlloc (sizeof(CLASSCONTAINER));
|
|
if (!pCS)
|
|
return NULL;
|
|
|
|
pCS->gpClassStore = NULL;
|
|
pCS->cBindFailures = 0;
|
|
pCS->cAccess = 0;
|
|
pCS->cNotFound = 0;
|
|
pCS->pszClassStorePath = (LPOLESTR)CoTaskMemAlloc
|
|
(sizeof(WCHAR) * (wcslen(pszPath)+1));
|
|
if (!(pCS->pszClassStorePath))
|
|
{
|
|
CoTaskMemFree(pCS);
|
|
return NULL;
|
|
}
|
|
|
|
wcscpy (pCS->pszClassStorePath, pszPath);
|
|
//++cStores;
|
|
|
|
return pCS;
|
|
}
|
|
|
|
|
|
extern WCHAR pwszDebugPath [];
|
|
extern BOOL fDebugPath;
|
|
|
|
//
|
|
// GetPerUserClassStore
|
|
// ---------------------
|
|
//
|
|
// Synopsis: Gets the ADT Class Store List from the
|
|
// per-user Registry.
|
|
// Returns error if none defined,
|
|
//
|
|
// Arguments:
|
|
// [out] ppStoreList : where to store list of class container
|
|
// serial numbers
|
|
// [out] pcStores : where to store number of class containers
|
|
//
|
|
// Returns: S_OK,
|
|
//
|
|
// History: Changed by (DebiM)
|
|
// 2/24/97
|
|
// return a NULL list of Class Stores when none defined.
|
|
//
|
|
|
|
HRESULT GetPerUserClassStore(
|
|
PSID pSid,
|
|
UINT CallType,
|
|
LPOLESTR **ppStoreList,
|
|
DWORD *pcStores)
|
|
|
|
{
|
|
LONG lErrorCode;
|
|
DWORD dwDataLen = 0;
|
|
DWORD dwType;
|
|
HKEY hKey = NULL;
|
|
HRESULT hr = S_OK;
|
|
LPOLESTR pszPath, pszStart;
|
|
LPOLESTR *ppszPath;
|
|
LPWSTR pszPathList=NULL;
|
|
|
|
*pcStores = 0;
|
|
*ppStoreList = NULL;
|
|
|
|
if (!fDebugPath)
|
|
{
|
|
switch (CallType)
|
|
{
|
|
case CS_CALL_LOCALSYSTEM :
|
|
CSDBGPrint((L"Reading HKLM for class store path"));
|
|
break;
|
|
|
|
case CS_CALL_IMPERSONATED :
|
|
CSDBGPrint((L"Reading as an impersonated user for class store path"));
|
|
break;
|
|
|
|
case CS_CALL_USERPROCESS :
|
|
CSDBGPrint((L"Reading HKCU for class store path"));
|
|
break;
|
|
|
|
default:
|
|
return E_FAIL;
|
|
}
|
|
|
|
HRESULT hrCSPath;
|
|
|
|
hrCSPath = ReadClassStorePath(
|
|
CallType != CS_CALL_LOCALSYSTEM ? pSid : NULL,
|
|
&pszPathList);
|
|
|
|
if (FAILED(hrCSPath))
|
|
{
|
|
// treat as NULL list of Class Stores
|
|
delete [] pszPathList;
|
|
return S_OK;
|
|
}
|
|
}
|
|
else // Test Mode - Privately Set Path - Only for testing
|
|
{
|
|
wcscpy (&pszPathList[0], &pwszDebugPath[0]);
|
|
}
|
|
|
|
// counting the number of ';'s and the number of class stores.
|
|
// assuming that it ends with a ;
|
|
|
|
DWORD cTentativeStores = 0;
|
|
|
|
for (pszPath = pszPathList, cTentativeStores = 0;
|
|
(pszPath = wcschr(pszPath, L';'));)
|
|
{
|
|
++(cTentativeStores); pszPath++;
|
|
}
|
|
|
|
++(cTentativeStores);
|
|
|
|
pszPath = pszPathList;
|
|
|
|
ppszPath = *ppStoreList = (LPOLESTR *) CoTaskMemAlloc
|
|
(sizeof(LPOLESTR) * (cTentativeStores));
|
|
|
|
if (*ppStoreList == NULL)
|
|
{
|
|
return E_OUTOFMEMORY;
|
|
}
|
|
|
|
memset (*ppStoreList, 0, sizeof(LPOLESTR) * (cTentativeStores));
|
|
|
|
//
|
|
// Parse the list to separate different class containers
|
|
//
|
|
|
|
while (*pszPath)
|
|
{
|
|
while (*pszPath == L' ')
|
|
++pszPath;
|
|
pszStart = pszPath;
|
|
|
|
if (!*pszPath)
|
|
break;
|
|
if (*pszPath == L';')
|
|
{
|
|
++pszPath;
|
|
continue;
|
|
}
|
|
|
|
while (*pszPath && (*pszPath != L';'))
|
|
++pszPath;
|
|
|
|
//
|
|
// got one. save it.
|
|
//
|
|
*ppszPath = (LPOLESTR) CoTaskMemAlloc (sizeof(WCHAR) * (ULONG) (pszPath - pszStart + 1));
|
|
|
|
if (!(*ppszPath))
|
|
ERROR_ON_FAILURE(hr=E_OUTOFMEMORY);
|
|
|
|
memcpy (*ppszPath, pszStart, sizeof (WCHAR) * (ULONG) (pszPath - pszStart));
|
|
*((*ppszPath)+(pszPath - pszStart)) = NULL;
|
|
|
|
(ppszPath)++;
|
|
|
|
if (*pszPath == L';')
|
|
{
|
|
++pszPath;
|
|
}
|
|
|
|
(*pcStores)++;
|
|
|
|
if ((*pcStores) == cTentativeStores)
|
|
break;
|
|
}
|
|
|
|
delete [] pszPathList;
|
|
return S_OK;
|
|
|
|
Error_Cleanup:
|
|
DWORD i;
|
|
for (i = 0; i < (*pcStores); i++)
|
|
{
|
|
if (ppszPath[i])
|
|
CoTaskMemFree(ppszPath[i]);
|
|
}
|
|
CoTaskMemFree(ppszPath);
|
|
|
|
ppStoreList = NULL;
|
|
|
|
(*pcStores) = 0;
|
|
if (pszPathList)
|
|
CoTaskMemFree(pszPathList);
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
//
|
|
// GetUserClassStores
|
|
// ------------------
|
|
//
|
|
// Synopsis: This routine reads the Class Store list and parses it.
|
|
// If it has prior knowledge it reurns the parsed list.
|
|
// Arguments:
|
|
// [out] pcStores: Number of Class Stores
|
|
// [out] ppStoreIdList: Class Store Id List,
|
|
//
|
|
// Returns: S_OK
|
|
// May return a NULL list of Class Stores.
|
|
//
|
|
//
|
|
|
|
|
|
HRESULT GetUserClassStores(
|
|
PCLASSCONTAINER **ppStoreList,
|
|
DWORD *pcStores,
|
|
BOOL *pfCache,
|
|
PSID *ppUserSid)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
UINT CallType;
|
|
PCLASSCONTAINER *pList = NULL;
|
|
DWORD i;
|
|
|
|
//
|
|
// Get the SID of the calling process
|
|
//
|
|
|
|
hr = GetUserSid(ppUserSid, &CallType);
|
|
|
|
if (FAILED(hr))
|
|
{
|
|
*ppUserSid = NULL;
|
|
hr = S_OK;
|
|
}
|
|
|
|
*pfCache = (CallType == CS_CALL_IMPERSONATED);
|
|
|
|
if (gDebug)
|
|
{
|
|
if (*pfCache)
|
|
CSDBGPrint((L"Cache the class store"));
|
|
}
|
|
|
|
EnterCriticalSection (&ClassStoreBindList);
|
|
|
|
//
|
|
// Get the Class Store List
|
|
//
|
|
LPOLESTR *ppStoreNameList = NULL;
|
|
|
|
hr = GetPerUserClassStore(
|
|
*ppUserSid, CallType, &ppStoreNameList, pcStores);
|
|
|
|
//
|
|
// Note that the above may return a NULL list of Class Stores
|
|
//
|
|
|
|
CSDBGPrint((L"Found %d ClassStores", (*pcStores)));
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
*ppStoreList = pList = (PCLASSCONTAINER *)
|
|
CoTaskMemAlloc (sizeof(PCLASSCONTAINER) * (*pcStores));
|
|
|
|
if (!(*ppStoreList))
|
|
hr = E_OUTOFMEMORY;
|
|
else
|
|
memset(pList, 0, sizeof(PCLASSCONTAINER) * (*pcStores));
|
|
}
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
for (i=0; i < (*pcStores); i++)
|
|
{
|
|
*pList = GetClassStore (ppStoreNameList[i]);
|
|
if (!(*pList))
|
|
{
|
|
// free all the ones that have been allocated.
|
|
DWORD j;
|
|
for (j = 0; j < (*pcStores); j++)
|
|
if (*pList)
|
|
{
|
|
if ((*pList)->pszClassStorePath)
|
|
CoTaskMemFree((*pList)->pszClassStorePath);
|
|
CoTaskMemFree(*pList);
|
|
}
|
|
|
|
hr = E_OUTOFMEMORY;
|
|
(*pcStores) = 0;
|
|
break;
|
|
}
|
|
|
|
pList++;
|
|
}
|
|
}
|
|
|
|
if (ppStoreNameList)
|
|
{
|
|
for (i=0; i < (*pcStores); ++i)
|
|
{
|
|
if (ppStoreNameList[i])
|
|
CoTaskMemFree (ppStoreNameList[i]);
|
|
}
|
|
}
|
|
|
|
if (ppStoreNameList)
|
|
CoTaskMemFree (ppStoreNameList);
|
|
|
|
LeaveCriticalSection (&ClassStoreBindList);
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
|