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