//************************************************************* // // Hash table for registry Rsop data // // Microsoft Confidential // Copyright (c) Microsoft Corporation 1999 // All rights reserved // // History: 7-Jun-99 SitaramR Created // //************************************************************* #include "uenv.h" #include "reghash.h" #include "rsop.h" #include REGKEYENTRY * AllocRegKeyEntry( WCHAR *pwszKeyName ); void FreeRegKeyEntry( REGKEYENTRY *pKeyEntry ); REGVALUEENTRY *AllocValueEntry( WCHAR *pwszValueName ); void FreeValueEntry( REGVALUEENTRY *pValueEntry ); REGDATAENTRY * AllocDataEntry( REGOPERATION opnType, DWORD dwType, DWORD dwLen, BYTE *pData, WCHAR *pwszGPO, WCHAR *pwszSOM, WCHAR *pwszCommand); void FreeDataEntry( REGDATAENTRY *pDataEntry ); BOOL DeleteRegTree( REGHASHTABLE *pHashTable, WCHAR *pwszKeyName, WCHAR *pwszGPO, WCHAR *pwszSOM, WCHAR *szCommand); REGKEYENTRY * FindRegKeyEntry( REGHASHTABLE *pHashTable, WCHAR *pwszKeyName, BOOL bCreate ); REGVALUEENTRY * FindValueEntry( REGHASHTABLE *pHashTable, WCHAR *pwszKeyName, WCHAR *pwszValueName, BOOL bCreate ); BOOL AddDataEntry( REGVALUEENTRY *pValueEntry, REGOPERATION opnType, DWORD dwType, DWORD dwLen, BYTE *pData, WCHAR *pwszGPO, WCHAR *pwszSOM, WCHAR *pwszCommand); //////////////////////////////////////////////////////////////////////// // Hash Table for registry policies // ---------------------------------- // // This hash table is used to log rsop data for registry policies. // A hash table entry is created for each registry entry. The registry entry // name itself is used to calculate the hash table. // // Each Registry entry has a link to each of the values modified by policy. // These values are in a link list and sorted by the valueNames. // // Each Value has the list of Data that are being set on the Values. This // sorted by the order of execution. The topmost value will contain the final value. // The Data entries have fields that mark the value as deleted and the Command // associated with the action. To look for the possible commands look in the // ParseRegistryFile. // // Additionally, in the hash table 2 special case values exist. // a. **Command Value. The Data under this value will contain all the commands // that are executed under this key. // // b. An ""(Empty ValueName) This valuename represents the modifications happening // to the key itself. For example a key can deleted or added.. // // Note: // The szCommand that is passed in has to be non NULL but can be an empty string. // There is a dependency on it in AddDataEntry and in logger.cpp. There is an Assert // for this in AddRegHashEntry // //////////////////////////////////////////////////////////////////////// //************************************************************* // // AllocHashTable // // Purpose: Allocates a new hash table // // Returns: Pointer to hash table // //************************************************************* REGHASHTABLE * AllocHashTable() { DWORD i; REGHASHTABLE *pHashTable = (REGHASHTABLE *) LocalAlloc (LPTR, sizeof(REGHASHTABLE)); if ( pHashTable == NULL ) { DebugMsg((DM_WARNING, TEXT("AllocHashTable: Failed to alloc hashtable."))); return NULL; } for ( i=0; iaHashTable[i] = 0; } pHashTable->hrError = S_OK; return pHashTable; } //************************************************************* // // FreeHashTable // // Purpose: Deletes a hash table // // Parameters: pHashTable - Hash table to delete // //************************************************************* void FreeHashTable( REGHASHTABLE *pHashTable ) { DWORD i; if ( pHashTable == NULL ) return; for ( i=0; iaHashTable[i]; while ( pKeyEntry ) { REGKEYENTRY *pNext = pKeyEntry->pNext; FreeRegKeyEntry( pKeyEntry ); pKeyEntry = pNext; } } } //************************************************************* // // AllocRegKey // // Purpose: Allocates a new registry key entry // // Returns: Pointer to registr key entry // //************************************************************* REGKEYENTRY * AllocRegKeyEntry( WCHAR *pwszKeyName ) { REGKEYENTRY *pKeyEntry = (REGKEYENTRY *) LocalAlloc (LPTR, sizeof(REGKEYENTRY)); if ( pKeyEntry == NULL ) { DebugMsg((DM_WARNING, TEXT("AllocRegKeyEntry: Failed to alloc key entry."))); return NULL; } DWORD dwKeyNameLength = lstrlen(pwszKeyName) + 1; pKeyEntry->pwszKeyName = (WCHAR *) LocalAlloc (LPTR, ( dwKeyNameLength ) * sizeof(WCHAR)); if ( pKeyEntry->pwszKeyName == NULL ) { DebugMsg((DM_WARNING, TEXT("AllocRegKeyEntry: Failed to alloc key name."))); LocalFree( pKeyEntry ); return NULL; } HRESULT hr = StringCchCopy( pKeyEntry->pwszKeyName, dwKeyNameLength, pwszKeyName ); if(FAILED(hr)){ LocalFree( pKeyEntry->pwszKeyName ); LocalFree( pKeyEntry ); return NULL; } return pKeyEntry; } //************************************************************* // // FreeRegKeyEntry // // Purpose: Deletes a registry key entry // // Parameters: pKeyEntry - Entry to delete // //************************************************************* void FreeRegKeyEntry( REGKEYENTRY *pKeyEntry ) { REGVALUEENTRY *pValueEntry = NULL; if ( pKeyEntry == NULL ) return; LocalFree( pKeyEntry->pwszKeyName ); pValueEntry = pKeyEntry->pValueList; while ( pValueEntry ) { REGVALUEENTRY *pNext = pValueEntry->pNext; FreeValueEntry( pValueEntry ); pValueEntry = pNext; } LocalFree( pKeyEntry ); } //************************************************************* // // AllocValueEntry // // Purpose: Allocates a new value entry // // Returns: Pointer to value entry // //************************************************************* REGVALUEENTRY *AllocValueEntry( WCHAR *pwszValueName ) { REGVALUEENTRY *pValueEntry = (REGVALUEENTRY *) LocalAlloc (LPTR, sizeof(REGVALUEENTRY)); if ( pValueEntry == NULL ) { DebugMsg((DM_WARNING, TEXT("AllocValueEntry: Failed to alloc value entry."))); return NULL; } DWORD dwValNameLength = lstrlen(pwszValueName) + 1; pValueEntry->pwszValueName = (WCHAR *) LocalAlloc (LPTR, ( dwValNameLength ) * sizeof(WCHAR)); if ( pValueEntry->pwszValueName == NULL ) { DebugMsg((DM_WARNING, TEXT("AllocValueEntry: Failed to alloc key name."))); LocalFree( pValueEntry ); return NULL; } HRESULT hr = StringCchCopy( pValueEntry->pwszValueName, dwValNameLength, pwszValueName ); if(FAILED(hr)){ LocalFree( pValueEntry->pwszValueName ); LocalFree( pValueEntry ); return NULL; } return pValueEntry; } //************************************************************* // // FreeValueEntry // // Purpose: Deletes a value entry // // Parameters: pValueEntry - Entry to delete // //************************************************************* void FreeValueEntry( REGVALUEENTRY *pValueEntry ) { REGDATAENTRY *pDataEntry = NULL; if ( pValueEntry == NULL ) return; LocalFree( pValueEntry->pwszValueName ); pDataEntry = pValueEntry->pDataList; while ( pDataEntry ) { REGDATAENTRY *pNext = pDataEntry->pNext; FreeDataEntry( pDataEntry ); pDataEntry = pNext; } LocalFree( pValueEntry ); } //************************************************************* // // AllocDataEntry // // Purpose: Allocates a new data entry // // Returns: Pointer to data entry // //************************************************************* REGDATAENTRY * AllocDataEntry( REGOPERATION opnType, DWORD dwType, DWORD dwLen, BYTE *pData, WCHAR *pwszGPO, WCHAR *pwszSOM, WCHAR *pwszCommand) { BOOL bResult = FALSE; REGDATAENTRY *pDataEntry = (REGDATAENTRY *) LocalAlloc (LPTR, sizeof(REGDATAENTRY)); if ( pDataEntry == NULL ) { DebugMsg((DM_WARNING, TEXT("AllocDataEntry: Failed to alloc data entry."))); return NULL; } if ( opnType == REG_ADDVALUE ) pDataEntry->bDeleted = FALSE; else pDataEntry->bDeleted = TRUE; pDataEntry->bAdmPolicy = FALSE; pDataEntry->dwValueType = dwType; pDataEntry->dwDataLen = dwLen; if ( pData ) { pDataEntry->pData = (BYTE *) LocalAlloc (LPTR, dwLen); if ( pDataEntry->pData == NULL ) { DebugMsg((DM_WARNING, TEXT("AllocDataEntry: Failed to alloc data."))); goto Exit; } CopyMemory( pDataEntry->pData, pData, dwLen ); } DmAssert( pwszGPO != NULL && pwszSOM != NULL ); DWORD dwGPOLength = lstrlen(pwszGPO) + 1; pDataEntry->pwszGPO = (WCHAR *) LocalAlloc (LPTR, ( dwGPOLength ) * sizeof(WCHAR)); if ( pDataEntry->pwszGPO == NULL ) { DebugMsg((DM_WARNING, TEXT("AllocDataEntry: Failed to alloc Gpo name."))); goto Exit; } HRESULT hr = StringCchCopy( pDataEntry->pwszGPO, dwGPOLength, pwszGPO ); if(FAILED(hr)) goto Exit; DWORD dwSOMLength = lstrlen(pwszSOM) + 1; pDataEntry->pwszSOM = (WCHAR *) LocalAlloc (LPTR, ( dwSOMLength ) * sizeof(WCHAR)); if ( pDataEntry->pwszSOM == NULL ) { DebugMsg((DM_WARNING, TEXT("AllocDataEntry: Failed to alloc Sdou name."))); goto Exit; } hr = StringCchCopy( pDataEntry->pwszSOM, dwSOMLength, pwszSOM ); if(FAILED(hr)) goto Exit; DWORD dwCmdLength = lstrlen(pwszCommand) + 1; pDataEntry->pwszCommand = (WCHAR *) LocalAlloc (LPTR, ( dwCmdLength ) * sizeof(WCHAR)); if ( pDataEntry->pwszCommand == NULL ) { DebugMsg((DM_WARNING, TEXT("AllocDataEntry: Failed to alloc Sdou name."))); goto Exit; } hr = StringCchCopy( pDataEntry->pwszCommand, dwCmdLength, pwszCommand ); if(FAILED(hr)) goto Exit; bResult = TRUE; Exit: if ( !bResult ) { LocalFree( pDataEntry->pData ); LocalFree( pDataEntry->pwszGPO ); LocalFree( pDataEntry->pwszSOM ); if (pDataEntry->pwszCommand) LocalFree(pDataEntry->pwszCommand); LocalFree( pDataEntry); return NULL; } return pDataEntry; } //************************************************************* // // FreeDataEntry // // Purpose: Deletes a data entry // // Parameters: pDataEntry - Entry to delete // //************************************************************* void FreeDataEntry( REGDATAENTRY *pDataEntry ) { if ( pDataEntry ) { LocalFree( pDataEntry->pData ); LocalFree( pDataEntry->pwszGPO ); LocalFree( pDataEntry->pwszSOM ); LocalFree( pDataEntry); } } //************************************************************* // // Hash // // Purpose: Maps a key name to a hash bucket // // Parameters: pwszName - Key name // // Returns: Hash bucket // //************************************************************* DWORD Hash( WCHAR *pwszName ) { DWORD dwLen = lstrlen( pwszName ); DWORD dwHashValue = 0; for ( ; dwLen>0; pwszName++ ) { dwHashValue = toupper(*pwszName) + 31 * dwHashValue; dwLen--; } return dwHashValue % HASH_TABLE_SIZE; } //************************************************************* // // AddRegHashEntry // // Purpose: Adds a registry key to the hash table // // Parameters: pwszName - Key name // //************************************************************* BOOL AddRegHashEntry( REGHASHTABLE *pHashTable, REGOPERATION opnType, WCHAR *pwszKeyName, WCHAR *pwszValueName, DWORD dwType, DWORD dwDataLen, BYTE *pData, WCHAR *pwszGPO, WCHAR *pwszSOM, WCHAR *szCommand, BOOL bCreateCommand) { REGVALUEENTRY *pValueEntry = NULL; BOOL bResult = FALSE; REGKEYENTRY *pKeyEntry=NULL; switch (opnType) { case REG_DELETEKEY: bResult = DeleteRegTree( pHashTable, pwszKeyName, pwszGPO, pwszSOM, szCommand ); break; case REG_INTERNAL_DELETESINGLEKEY: case REG_DELETEALLVALUES: pKeyEntry = FindRegKeyEntry( pHashTable, pwszKeyName, FALSE ); if ( pKeyEntry == NULL ) { // // Delete all values is similar to policy being disabled and // so do nothing. // if (opnType == REG_DELETEALLVALUES) { bResult = TRUE; break; } else // no command entry in this case. return TRUE; } pValueEntry = pKeyEntry->pValueList; while ( pValueEntry ) { if (lstrcmp(pValueEntry->pwszValueName, TEXT("")) != 0) { if (CompareString(LOCALE_INVARIANT, NORM_IGNORECASE, pValueEntry->pwszValueName, -1, STARCOMMAND, -1) != CSTR_EQUAL) { // // Mark the value as deleted // bResult = AddDataEntry( pValueEntry, opnType, 0, 0, NULL, pwszGPO, pwszSOM, szCommand ); if ( !bResult ) return FALSE; } } else { // // Mark the key as deleted // if (opnType == REG_INTERNAL_DELETESINGLEKEY) { bResult = AddDataEntry( pValueEntry, opnType, 0, 0, NULL, pwszGPO, pwszSOM, szCommand ); if ( !bResult ) return FALSE; } } pValueEntry = pValueEntry->pNext; } bResult = TRUE; break; case REG_ADDVALUE: case REG_SOFTADDVALUE: // // We have to make a value with no name to represent the creation of key itself.. // pValueEntry = FindValueEntry( pHashTable, pwszKeyName, TEXT(""), TRUE ); if ( pValueEntry == NULL ) return FALSE; bResult = AddDataEntry( pValueEntry, opnType, 0, 0, NULL, pwszGPO, pwszSOM, szCommand ); if (!bResult) return FALSE; if ((!pwszValueName) || (!(*pwszValueName)) || (dwDataLen == 0) || (dwType == REG_NONE)) break; // fall through case REG_DELETEVALUE: pValueEntry = FindValueEntry( pHashTable, pwszKeyName, pwszValueName, TRUE ); if ( pValueEntry == NULL ) return FALSE; // // In case of SOFTADDVALUE the final decision to add the value is made in // AddDataEntry // bResult = AddDataEntry( pValueEntry, opnType, dwType, dwDataLen, pData, pwszGPO, pwszSOM, szCommand ); break; default: DmAssert(FALSE && "Unknown Case Selector for AddRegHashEntry"); } DmAssert(szCommand); // // If everything succeeded, then log the command if // bCreateCommand is true. This is done creating or adding // to a value called **Command. This means that this value is not // Settable by adm file.. // if ((bResult) && (bCreateCommand) && (opnType != REG_INTERNAL_DELETESINGLEKEY) && (*szCommand != TEXT('\0'))) { pValueEntry = FindValueEntry( pHashTable, pwszKeyName, STARCOMMAND, TRUE ); if ( pValueEntry == NULL ) return FALSE; bResult = AddDataEntry( pValueEntry, REG_ADDVALUE, 0, sizeof(TCHAR)*(lstrlen(szCommand)+1), (BYTE *)szCommand, pwszGPO, pwszSOM, szCommand); } return bResult; } //************************************************************* // // DeleteRegTree // // Purpose: Deletes a key and all its subkeys // // Parameters: pHashTable - Hash table // pwszKeyName - Key name to delete // pwszGPO - Gpo // pwszSOM - Sdou that the Gpo is linked to // //************************************************************* BOOL DeleteRegTree( REGHASHTABLE *pHashTable, WCHAR *pwszKeyName, WCHAR *pwszGPO, WCHAR *pwszSOM, WCHAR *szCommand) { DWORD i=0; DWORD dwKeyLen = lstrlen( pwszKeyName ); for ( i=0; iaHashTable[i]; while ( pKeyEntry ) { BOOL bAdd = FALSE; DWORD dwKeyLen2 = lstrlen(pKeyEntry->pwszKeyName); if ( dwKeyLen2 >= dwKeyLen && CompareString (LOCALE_USER_DEFAULT, NORM_IGNORECASE, pKeyEntry->pwszKeyName, dwKeyLen, pwszKeyName, dwKeyLen ) == CSTR_EQUAL) { // // It's a prefix if length and strings match, or if one // string is bigger and there is a '\' at the right place. // if ( dwKeyLen2 > dwKeyLen ) { if ( pKeyEntry->pwszKeyName[dwKeyLen] == L'\\' ) bAdd = TRUE; } else bAdd = TRUE; if ( bAdd ) { BOOL bResult = AddRegHashEntry( pHashTable, REG_INTERNAL_DELETESINGLEKEY, pKeyEntry->pwszKeyName, NULL, 0, 0, NULL, pwszGPO, pwszSOM, szCommand, FALSE ); if ( !bResult ) return FALSE; } } // if dwKeyLen2 >= dwKeyLen pKeyEntry = pKeyEntry->pNext; } // while } // for return TRUE; } //************************************************************* // // FindRegKeyEntry // // Purpose: Looks up a reg key entry in hash table // // Parameters: pHashTable - Hash table // pwszKeyName - Key name to find // bCreate - Should key be created if not found ? // //************************************************************* REGKEYENTRY * FindRegKeyEntry( REGHASHTABLE *pHashTable, WCHAR *pwszKeyName, BOOL bCreate ) { DWORD dwHashValue = Hash( pwszKeyName ); REGKEYENTRY *pCurPtr = pHashTable->aHashTable[dwHashValue]; REGKEYENTRY *pTrailPtr = NULL; while ( pCurPtr != NULL ) { INT iResult = CompareString( LOCALE_USER_DEFAULT, NORM_IGNORECASE, pwszKeyName, -1, pCurPtr->pwszKeyName, -1 ); if ( iResult == CSTR_EQUAL ) { return pCurPtr; } else if ( iResult == CSTR_LESS_THAN ) { // // Keys are in ascending order, so insert if bCreate // if ( bCreate ) { REGKEYENTRY *pKeyEntry = AllocRegKeyEntry( pwszKeyName ); if ( pKeyEntry == NULL ) return 0; pKeyEntry->pNext = pCurPtr; if ( pTrailPtr == NULL ) pHashTable->aHashTable[dwHashValue] = pKeyEntry; else pTrailPtr->pNext = pKeyEntry; return pKeyEntry; } else return NULL; } else { // // Advance down the list // pTrailPtr = pCurPtr; pCurPtr = pCurPtr->pNext; } } // // End of list or null list case // if ( bCreate ) { REGKEYENTRY *pKeyEntry = AllocRegKeyEntry( pwszKeyName ); if ( pKeyEntry == NULL ) return 0; pKeyEntry->pNext = 0; if ( pTrailPtr == NULL ) pHashTable->aHashTable[dwHashValue] = pKeyEntry; else pTrailPtr->pNext = pKeyEntry; return pKeyEntry; } return NULL; } //************************************************************* // // FindValueEntry // // Purpose: Looks up a value entry in hash table // // Parameters: pHashTable - Hash table // pwszKeyName - Key name to find // pwszValueName - Value name to find // bCreate - Should key be created if not found ? // //************************************************************* REGVALUEENTRY * FindValueEntry( REGHASHTABLE *pHashTable, WCHAR *pwszKeyName, WCHAR *pwszValueName, BOOL bCreate ) { REGVALUEENTRY *pCurPtr = NULL; REGVALUEENTRY *pTrailPtr = NULL; REGKEYENTRY *pKeyEntry = FindRegKeyEntry( pHashTable, pwszKeyName, bCreate ); if ( pKeyEntry == NULL ) return NULL; pCurPtr = pKeyEntry->pValueList; pTrailPtr = NULL; while ( pCurPtr != NULL ) { INT iResult = CompareString( LOCALE_USER_DEFAULT, NORM_IGNORECASE, pwszValueName, -1, pCurPtr->pwszValueName, -1 ); if ( iResult == CSTR_EQUAL ) { return pCurPtr; } else if ( iResult == CSTR_LESS_THAN ) { // // Keys are in ascending order, so insert if bCreate // if ( bCreate ) { REGVALUEENTRY *pValueEntry = AllocValueEntry( pwszValueName ); if ( pValueEntry == NULL ) return 0; pValueEntry->pNext = pCurPtr; if ( pTrailPtr == NULL ) pKeyEntry->pValueList = pValueEntry; else pTrailPtr->pNext = pValueEntry; return pValueEntry; } else return NULL; } else { // // Advance down the list // pTrailPtr = pCurPtr; pCurPtr = pCurPtr->pNext; } } // // End of list or null list case // if ( bCreate ) { REGVALUEENTRY *pValueEntry = AllocValueEntry( pwszValueName ); if ( pValueEntry == NULL ) return 0; pValueEntry->pNext = 0; if ( pTrailPtr == NULL ) pKeyEntry->pValueList = pValueEntry; else pTrailPtr->pNext = pValueEntry; return pValueEntry; } return NULL; } //************************************************************* // // AddDataEntry // // Purpose: Adds a data entry to a value entry struct // // Parameters: pValueEntry - Value entry // opnType - Operation type // dwType - Type of registry data // dwLen - Length of registry data // pData - Data // pwszGPO - Gpo that set this value // pwszSOM - Sdou that the Gpo is linked to // //************************************************************* BOOL AddDataEntry( REGVALUEENTRY *pValueEntry, REGOPERATION opnType, DWORD dwType, DWORD dwLen, BYTE *pData, WCHAR *pwszGPO, WCHAR *pwszSOM, WCHAR *pwszCommand) { REGDATAENTRY *pDataEntry = NULL; if (opnType == REG_SOFTADDVALUE) { // // if the data list is null or if the first value (highest precedence value is deleted) // then add it to the list // if ((pValueEntry->pDataList == NULL) || (pValueEntry->pDataList->pNext->bDeleted)) opnType = REG_ADDVALUE; else return TRUE; // return without adding the value. } pDataEntry = AllocDataEntry( opnType, dwType, dwLen, pData, pwszGPO, pwszSOM, pwszCommand ); if ( pDataEntry == NULL ) return FALSE; // // Prepend to data list because entries at beginning of list have higher precedence // pDataEntry->pNext = pValueEntry->pDataList; pValueEntry->pDataList = pDataEntry; return TRUE; }