windows-nt/Source/XPSP1/NT/com/ole32/cs/cstore/csuser.cxx
2020-09-26 16:20:57 +08:00

968 lines
23 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"
void GetCurrentUsn(LPOLESTR pStoreUsn);
//
// 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 *) PrivMemAlloc( 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 );
PrivMemFree( pwszKey );
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 };
#define CS_CALL_LOCALSYSTEM 1
#define CS_CALL_USERPROCESS 2
#define CS_CALL_IMPERSONATED 3
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 (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);
}
if (hUserToken)
CloseHandle( hUserToken );
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
// GetKnownClassStore
// -------------------
//
//
// Synopsis: Gets a class container path.
// Looks up list of containers seen
// and returns the pointer for this container.
// If a new class container is seen,
// it is added to this list and its pointer is returned.
//
// Arguments: [in] pszPath - Class container Path
// Returns: pClassStoreNode : Class Container Node
//
//
PCLASSCONTAINER
GetKnownClassStore (LPOLESTR pszPath)
{
PCLASSCONTAINER pCS = gpContainerHead;
//
// Chain thru the link list of containers ...
//
while (pCS != NULL)
{
if (!wcscmp (pszPath, pCS->pszClassStorePath))
{
break;
}
pCS = pCS->pNextClassStore;
}
//
// If not matched ..
// Add it to the beginning of the list.
//
if (pCS == NULL)
{
pCS = (CLASSCONTAINER *) CoTaskMemAlloc (sizeof(CLASSCONTAINER));
pCS->pNextClassStore = gpContainerHead;
gpContainerHead = pCS;
pCS->gpClassStore = NULL;
pCS->cBindFailures = 0;
pCS->cAccess = 0;
pCS->cNotFound = 0;
pCS->pszClassStorePath = (LPOLESTR)CoTaskMemAlloc
(sizeof(WCHAR) * (wcslen(pszPath)+1));
wcscpy (pCS->pszClassStorePath, pszPath);
//++cStores;
}
return pCS;
}
/*
//
// GetUserSyncPoint
// ----------------
//
// Synopsis: Receives and Stores the Next Sync Point.
// Reads and returns the current sync point.
// When Advance is called the current becomes the lastsyncpoint.
// No error returned,
//
// BUGBUG. This is NOT thread-safe now. Fix it!
//
HRESULT GetUserSyncPoint(LPWSTR pszContainer, CSUSN *pPrevUsn)
{
LONG lErrorCode;
DWORD dwDataLen = _MAX_PATH;
DWORD dwType;
HKEY hKey = NULL;
HRESULT hr = S_OK;
WCHAR wszSync[_MAX_PATH + 1];
PSID pUserSid;
CSUSN CurrUsn;
//
// Get the current USN
//
GetCurrentUsn(&CurrUsn);
//
// Get the SID of the calling process
//
hr = GetUserSid(&pUserSid);
RETURN_ON_FAILURE(hr);
//
// This should be outside of impersonation
// So revert to LOCAL SYSTEM.
RpcRevertToSelf();
lErrorCode = OpenUserRegKey(
pUserSid,
L"Software\\Microsoft\\ClassStore",
&hKey);
if (lErrorCode != ERROR_SUCCESS)
{
DWORD dwDisp;
lErrorCode = RegCreateKeyEx(HKEY_CURRENT_USER,
L"Software\\Microsoft\\ClassStore",
NULL,
L"REG_SZ",
REG_OPTION_NON_VOLATILE,
KEY_ALL_ACCESS,
NULL,
&hKey,
&dwDisp);
}
lErrorCode = RegQueryValueEx(hKey,
pszContainer,
NULL,
&dwType,
(LPBYTE)wszSync,
&dwDataLen);
if (lErrorCode != ERROR_SUCCESS)
{
pPrevUsn->dwLowDateTime = CurrUsn.dwLowDateTime;
pPrevUsn->dwHighDateTime = CurrUsn.dwHighDateTime;
wsprintf (wszSync, L"%lu %lu %lu %lu",
pPrevUsn->dwHighDateTime,
pPrevUsn->dwLowDateTime,
pPrevUsn->dwHighDateTime,
pPrevUsn->dwLowDateTime);
lErrorCode = RegSetValueEx(
hKey,
pszContainer, //L"LastSync",
NULL,
REG_SZ,
(LPBYTE)wszSync,
(2 * wcslen(wszSync)) + 2);
RegCloseKey(hKey);
}
else
{
swscanf (wszSync, L"%lu %lu",
&pPrevUsn->dwHighDateTime,
&pPrevUsn->dwLowDateTime);
wsprintf (wszSync, L"%lu %lu %lu %lu",
pPrevUsn->dwHighDateTime,
pPrevUsn->dwLowDateTime,
CurrUsn.dwHighDateTime,
CurrUsn.dwLowDateTime);
lErrorCode = RegSetValueEx(
hKey,
pszContainer, //L"LastSync",
NULL,
REG_SZ,
(LPBYTE)wszSync,
(2 * wcslen(wszSync)) + 2);
RegCloseKey(hKey);
}
// Impersonate again
RpcImpersonateClient((RPC_BINDING_HANDLE)0);
return S_OK;
}
//
// AdvanceUserSyncPoint
// --------------------
//
// Synopsis: Makes the Next Sync Point as Last Sync Point.
// No error returned,
//
//
HRESULT AdvanceUserSyncPoint(LPWSTR pszContainer)
{
LONG lErrorCode;
DWORD dwDataLen = _MAX_PATH;
DWORD dwType;
HKEY hKey = NULL;
HRESULT hr = S_OK;
WCHAR wszSync[_MAX_PATH + 1];
CSUSN PrevUsn, NextUsn;
PSID pUserSid;
//
// Get the SID of the calling process
// Already assumed to be under impersonation
//
hr = GetUserSid(&pUserSid);
RETURN_ON_FAILURE(hr);
//
// This should be outside of impersonation
// So revert to LOCAL SYSTEM.
RpcRevertToSelf();
lErrorCode = OpenUserRegKey(
pUserSid,
L"Software\\Microsoft\\ClassStore",
&hKey);
if ( lErrorCode == ERROR_SUCCESS)
{
lErrorCode = RegQueryValueEx(hKey,
pszContainer,
NULL,
&dwType,
(LPBYTE)wszSync,
&dwDataLen);
if (lErrorCode != ERROR_SUCCESS)
{
RegCloseKey(hKey);
}
}
if (lErrorCode == ERROR_SUCCESS)
{
swscanf (wszSync, L"%lu %lu %lu %lu",
&PrevUsn.dwHighDateTime,
&PrevUsn.dwLowDateTime,
&NextUsn.dwHighDateTime,
&NextUsn.dwLowDateTime);
wsprintf (wszSync, L"%lu %lu %lu %lu",
NextUsn.dwHighDateTime,
NextUsn.dwLowDateTime,
NextUsn.dwHighDateTime,
NextUsn.dwLowDateTime);
lErrorCode = RegSetValueEx(
hKey,
pszContainer, //L"LastSync",
NULL,
REG_SZ,
(LPBYTE)wszSync,
(2 * wcslen(wszSync)) + 2);
RegCloseKey(hKey);
}
// Impersonate again
RpcImpersonateClient((RPC_BINDING_HANDLE)0);
return S_OK;
}
*/
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.
//
#define MAXCLASSSTORES 10
HRESULT GetPerUserClassStore(
PSID pSid,
UINT CallType,
LPOLESTR **ppStoreList,
DWORD *pcStores)
{
LONG lErrorCode;
DWORD dwDataLen = 2000;
DWORD dwType;
HKEY hKey = NULL;
HRESULT hr = S_OK;
LPOLESTR pszPath, pszStart;
LPOLESTR *ppszPath;
WCHAR pszPathList [2000 + 1];
*pcStores = 0;
*ppStoreList = NULL;
if (!fDebugPath)
{
switch (CallType)
{
case CS_CALL_LOCALSYSTEM :
lErrorCode = RegOpenKeyEx(HKEY_LOCAL_MACHINE,
L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Group Policy",
NULL,
KEY_READ,
&hKey);
break;
case CS_CALL_IMPERSONATED :
lErrorCode = OpenUserRegKey(
pSid,
L"Software\\Microsoft\\Windows\\CurrentVersion\\Group Policy",
&hKey);
break;
case CS_CALL_USERPROCESS :
lErrorCode = RegOpenKeyEx(HKEY_CURRENT_USER,
L"Software\\Microsoft\\Windows\\CurrentVersion\\Group Policy",
NULL,
KEY_ALL_ACCESS,
&hKey);
break;
default:
return E_FAIL;
}
if ( lErrorCode != ERROR_SUCCESS)
{
// treat as NULL list of Class Stores
return S_OK;
}
lErrorCode = RegQueryValueEx(hKey,
L"ClassStorePath",
NULL,
&dwType,
(LPBYTE)pszPathList,
&dwDataLen);
RegCloseKey(hKey);
if (lErrorCode != ERROR_SUCCESS)
{
// treat as NULL list of Class Stores
return S_OK;
}
}
else // Test Mode - Privately Set Path - Only for testing
{
wcscpy (&pszPathList[0], &pwszDebugPath[0]);
}
pszPath = pszPathList;
ppszPath = *ppStoreList = (LPOLESTR *) CoTaskMemAlloc
(sizeof(LPOLESTR) * MAXCLASSSTORES);
if (*ppStoreList == NULL)
{
return E_OUTOFMEMORY;
}
//
// 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) * (pszPath - pszStart + 1));
memcpy (*ppszPath, pszStart, sizeof (WCHAR) * (pszPath - pszStart));
*((*ppszPath)+(pszPath - pszStart)) = NULL;
(ppszPath)++;
++(*pcStores);
if (*pszPath == L';')
{
++pszPath;
}
}
return S_OK;
}
//
// CacheSid
// ---------
//
// Synopsis: Gets a SID.
// Gets a list of class container paths for this SID.
// Looks up known class containers to map these to
// ClassStore Node pointers.
// Caches the SID and associated class store list.
//
// Arguments:
// [in] pUserSid: SID
// [in] ppStoreList: Class Store Path List,
// [in] cStores: Number of Class Stores
//
//
// Returns: ppStoreList: Class Store Node List
//
PCLASSCONTAINER * CacheSid (PSID pUserSid,
LPOLESTR *ppStoreList,
DWORD cStores)
{
ULONG i;
PCLASSCONTAINER *pStoreList, *pList;
USERPROFILE *pUser;
//
// Allocate a User structure
// and store the user specific values in it
//
pUser = (USERPROFILE *) CoTaskMemAlloc (sizeof(USERPROFILE));
if (pUserSid)
{
pUser->pCachedSid = (PSID) CoTaskMemAlloc (GetLengthSid(pUserSid));
CopySid(GetLengthSid(pUserSid),
pUser->pCachedSid,
pUserSid);
}
else
pUser->pCachedSid = NULL;
//
// Link it to the beginning of the User Linklist
//
pUser->pNextUser = gpUserHead;
gpUserHead = pUser;
//
// Find its ClassStore Path and setup the class store node chain
//
if (cStores == 0)
{
// NULL list of Class Stores
pStoreList = pUser->pUserStoreList = NULL;
}
else
{
pStoreList = pList =
pUser->pUserStoreList = (PCLASSCONTAINER *)
CoTaskMemAlloc (sizeof(PCLASSCONTAINER) * cStores);
for (i=0; i < cStores; i++)
{
*pList = GetKnownClassStore (*ppStoreList);
ppStoreList++;
pList++;
}
}
pUser->cUserStoreCount = cStores;
return pStoreList;
}
//
// GetUserClassStores
// ------------------
//
// Synopsis: This routine finds out the SID of the user.
// It then looks up a global cache of SIDs to see if it
// has accessed the list of Class Stores for this user.
//
// If not, it 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
// See change note for GetPerUserClassStore().
// May return a NULL list of Class Stores.
//
//
HRESULT GetUserClassStores(
PCLASSCONTAINER **ppStoreList,
DWORD *pcStores)
{
int l;
PSID pUserSid;
HRESULT hr = S_OK;
UINT CallType;
// Impersonate before accessing SID
//RpcImpersonateClient((RPC_BINDING_HANDLE)0);
//
// Get the SID of the calling process
//
hr = GetUserSid(&pUserSid, &CallType);
if (FAILED(hr))
{
pUserSid = NULL;
hr = S_OK;
}
//RpcRevertToSelf();
EnterCriticalSection (&ClassStoreBindList);
//
// Look this up in the global list of SIDs
//
USERPROFILE *pUser = gpUserHead;
while (pUser != NULL)
{
if ((pUserSid == pUser->pCachedSid) || // Null UserSid
(EqualSid(pUserSid, pUser->pCachedSid)))
//
// Found the match
//
{
*ppStoreList = pUser->pUserStoreList;
*pcStores = pUser->cUserStoreCount;
break;
}
pUser = pUser->pNextUser;
}
if (pUser == NULL)
{
//
// Didnt find this User's Sid
// Get the Class Store List and cache it along with this SID
//
LPOLESTR *ppStoreNameList = NULL;
UINT i;
hr = GetPerUserClassStore(
pUserSid, CallType, &ppStoreNameList, pcStores);
//
// Note that the above may return a NULL list of Class Stores
//
if (SUCCEEDED(hr))
{
*ppStoreList = CacheSid (pUserSid, ppStoreNameList, *pcStores);
//
// release memory allocated in GetPerUserClassStore()
//
for (i=0; i < *pcStores; ++i)
{
CoTaskMemFree (ppStoreNameList[i]);
}
if (ppStoreNameList)
CoTaskMemFree (ppStoreNameList);
}
}
LeaveCriticalSection (&ClassStoreBindList);
//
// Free the Sid
//
if (pUserSid)
CoTaskMemFree (pUserSid);
return hr;
}