/*++ Copyright (c) 1998-2001 Microsoft Corporation Module Name: migrate.c Abstract: This module contains the code necessary for Plug and Play to prepare the necessary state during a winnt32.exe upgrade or an ASR (Automated System Recovery) backup operation. Typically, these aspects of the Plug and Play registry are saved in a sif for later use, during the text-mode setup portion of an upgrade or ASR recovery operation. Author: Jim Cavalaris (jamesca) 07-Mar-2000 Environment: User-mode only. Revision History: 07-March-2000 jamesca Creation and initial implementation. --*/ // // includes // #include "precomp.h" #include "debug.h" #include "util.h" #include #include #include #include // // definitions // // do the lock/unlock Enum security thing? (as taken from PNPREG) #define DO_LOCK_UNLOCK 0 // // memory allocation macros // (always use LocalAlloc/LocalReAlloc so that the caller can LocalFree the // returned buffer.) // #define MyMalloc(size) LocalAlloc(0, size); #define MyFree(entry) LocalFree(entry); #define MyRealloc(entry,size) LocalReAlloc(entry, size, LMEM_MOVEABLE | LMEM_ZEROINIT); // // globals for the Enum branch lock/unlock and security routines - taken from PNPREG // (we only need these if we're doing the Enum lock/unlock thing) // #if DO_LOCK_UNLOCK // DO_LOCK_UNLOCK PSID g_pAdminSid; PSID g_pSystemSid; PSID g_pWorldSid; SECURITY_DESCRIPTOR g_DeviceParametersSD; PACL g_pDeviceParametersDacl; SECURITY_DESCRIPTOR g_LockedPrivateKeysSD; PACL g_pLockedPrivateKeysDacl; #if 0 //#if DBG // DBG TCHAR g_szCurrentKeyName[4096]; DWORD g_dwCurrentKeyNameLength = 0; #endif // DBG #endif // DO_LOCK_UNLOCK // // public prototypes // BOOL MigrateDeviceInstanceData( OUT LPTSTR *Buffer ); BOOL MigrateClassKeys( OUT LPTSTR *Buffer ); BOOL MigrateHashValues( OUT LPTSTR *Buffer ); // // private prototypes // BOOL EnumerateDeviceKeys( IN HKEY CCSEnumKey, IN LPTSTR Enumerator, IN OUT LPTSTR *pszDeviceInstanceSection, IN OUT LPTSTR *pszDeviceInstanceCurrent, IN OUT DWORD *dwDeviceInstanceSectionLength, IN OUT DWORD *dwDeviceInstanceSectionRemaining ); BOOL EnumerateInstanceKeys( IN HKEY EnumeratorKey, IN LPTSTR Enumerator, IN LPTSTR Device, IN OUT LPTSTR *pszDeviceInstanceSection, IN OUT LPTSTR *pszDeviceInstanceCurrent, IN OUT DWORD *dwDeviceInstanceSectionLength, IN OUT DWORD *dwDeviceInstanceSectionRemaining ); BOOL EnumerateClassSubkeys( IN HKEY ClassKey, IN LPTSTR ClassKeyName, IN OUT LPTSTR *pszClassKeySection, IN OUT LPTSTR *pszClassKeyCurrent, IN OUT DWORD *dwClassKeySectionLength, IN OUT DWORD *dwClassKeySectionRemaining ); BOOL CanStringBeMigrated( IN LPTSTR pszBuffer ); // we only need these if we're doing the Enum lock/unlock thing #if DO_LOCK_UNLOCK // DO_LOCK_UNLOCK VOID LockUnlockEnumTree( IN BOOL bLock ); VOID EnumKeysAndApplyDacls( IN HKEY hParentKey, IN LPTSTR pszKeyName, IN DWORD dwLevel, IN BOOL bInDeviceParameters, IN BOOL bApplyTopDown, IN PSECURITY_DESCRIPTOR pPrivateKeySD, IN PSECURITY_DESCRIPTOR pDeviceParametersSD ); BOOL CreateSecurityDescriptors( VOID ); VOID FreeSecurityDescriptors( VOID ); #endif // DO_LOCK_UNLOCK // // Device instance enumeration routines // BOOL MigrateDeviceInstanceData( OUT LPTSTR *Buffer ) /*++ Routine Description: This routine walks the Plug and Play Enum tree in the registry, and collects the data that is relevant to restoring this state during textmode setup. Specifically, a multi-sz string will be returned to the caller that contains migration data for the UniqueParentID, ParentIdPrefix, and Driver registry values for all device instances in the Enum tree. Arguments: Buffer - Supplies the address of a character pointer, that on success will contain a multi-sz list of device instances and relevant values to migrate. The caller is responsible for freeing the memory via LocalFree. Return Value: TRUE if successful, FALSE otherwise. Upon failure, additional information can be retrieved by calling GetLastError(). --*/ { LONG result = ERROR_SUCCESS; HKEY hEnumKey = NULL; DWORD dwSubkeyCount, dwMaxSubKeyLength, i; LPTSTR pszEnumerator = NULL; LPTSTR pszDeviceInstanceSection = NULL; LPTSTR pszDeviceInstanceCurrent = NULL; DWORD dwDeviceInstanceSectionLength = 0; DWORD dwDeviceInstanceSectionRemaining = 0; // // Initialize the output parameter. // *Buffer = NULL; #if DO_LOCK_UNLOCK // DO_LOCK_UNLOCK // // Unlock the Enum key. // LockUnlockEnumTree(FALSE); #endif // DO_LOCK_UNLOCK // // Allocate storage and initialize variables for the device instance // migration section. // if (pszDeviceInstanceSection == NULL) { dwDeviceInstanceSectionLength = dwDeviceInstanceSectionRemaining = 256; pszDeviceInstanceSection = MyMalloc(dwDeviceInstanceSectionLength * sizeof(TCHAR)); if (!pszDeviceInstanceSection) { DBGTRACE( DBGF_ERRORS, (TEXT("MigrateDeviceInstanceData: initial ALLOC for ClassKeySection failed!!\n") )); result = ERROR_NOT_ENOUGH_MEMORY; goto Clean0; } pszDeviceInstanceCurrent = pszDeviceInstanceSection; } // // Open a handle to the HKLM\SYSTEM\CCS\Enum key. // result = RegOpenKeyEx(HKEY_LOCAL_MACHINE, REGSTR_PATH_SYSTEMENUM, 0, KEY_READ, &hEnumKey); if (result != ERROR_SUCCESS) { DBGTRACE( DBGF_ERRORS, (TEXT("MigrateDeviceInstanceData: failed to open %s, error=0x%08lx\n"), REGSTR_PATH_SYSTEMENUM, result)); goto Clean0; } // // Query the Enum key for enumerator subkey information. // result = RegQueryInfoKey(hEnumKey, NULL, NULL, NULL, &dwSubkeyCount, &dwMaxSubKeyLength, NULL, NULL, NULL, NULL, NULL, NULL); if (result != ERROR_SUCCESS) { DBGTRACE( DBGF_ERRORS, (TEXT("MigrateDeviceInstanceData: failed to query %s key, error=0x%08lx\n"), REGSTR_PATH_SYSTEMENUM, result)); goto Clean0; } // // Allocate a buffer to hold the largest enumerator key name. // dwMaxSubKeyLength++; pszEnumerator = MyMalloc(dwMaxSubKeyLength * sizeof(TCHAR)); if (!pszEnumerator) { DBGTRACE( DBGF_ERRORS, (TEXT("MigrateDeviceInstanceData: failed to allocate buffer for Enum subkeys\n") )); result = ERROR_NOT_ENOUGH_MEMORY; goto Clean0; } // // Enumerate the enumerator subkeys. // for (i = 0; i < dwSubkeyCount; i++) { DWORD dwEnumeratorLength = dwMaxSubKeyLength; result = RegEnumKeyEx(hEnumKey, i, pszEnumerator, &dwEnumeratorLength, 0, NULL, NULL, NULL); if (result != ERROR_SUCCESS) { // // If there was some error enumerating this key, skip it. // MYASSERT(result != ERROR_NO_MORE_ITEMS); DBGTRACE( DBGF_WARNINGS, (TEXT("MigrateDeviceInstanceData: failed to enumerate an enumerator subkey, error=0x%08lx\n"), result)); result = ERROR_SUCCESS; continue; } // // Enumerate the devices and device instances for this enumerator, and // append the migration data for each to the section buffer. // if (!EnumerateDeviceKeys(hEnumKey, pszEnumerator, &pszDeviceInstanceSection, &pszDeviceInstanceCurrent, &dwDeviceInstanceSectionLength, &dwDeviceInstanceSectionRemaining)) { DBGTRACE( DBGF_ERRORS, (TEXT("MigrateDeviceInstanceData: EnumerateDeviceKeys failed, error=0x%08lx\n"), GetLastError())); } } // // Once we've enumerated all device instances, add the final NULL terminator // to the multi-sz buffer. There must be enough space for the final NULL // terminator because the buffer is always reallocated unless there is room. // MYASSERT(dwDeviceInstanceSectionRemaining > 0); MYASSERT(pszDeviceInstanceCurrent); *pszDeviceInstanceCurrent = TEXT('\0'); dwDeviceInstanceSectionRemaining -= 1; Clean0: // // Do some cleanup. // if (pszEnumerator) { MyFree(pszEnumerator); } if (hEnumKey) { RegCloseKey(hEnumKey); } #if DO_LOCK_UNLOCK // DO_LOCK_UNLOCK // // Lock the Enum tree. // LockUnlockEnumTree(TRUE); #endif // DO_LOCK_UNLOCK // // Return the buffer to the caller only if successful. // if (result == ERROR_SUCCESS) { *Buffer = pszDeviceInstanceSection; } else { SetLastError(result); if (pszDeviceInstanceSection) { MyFree(pszDeviceInstanceSection); } } return (result == ERROR_SUCCESS); } // MigrateDeviceInstanceData() BOOL EnumerateDeviceKeys( IN HKEY CCSEnumKey, IN LPTSTR Enumerator, IN OUT LPTSTR *pszDeviceInstanceSection, IN OUT LPTSTR *pszDeviceInstanceCurrent, IN OUT DWORD *dwDeviceInstanceSectionLength, IN OUT DWORD *dwDeviceInstanceSectionRemaining ) /*++ Routine Description: Enumerates device keys of an enumerator. Worker routine for MigrateDeviceInstanceData. Return Value: TRUE if successful, FALSE otherwise. Upon failure, additional information can be retrieved by calling GetLastError(). --*/ { LONG result; HKEY hEnumeratorKey = NULL; LPTSTR pszDeviceName = NULL; DWORD dwSubkeyCount, dwMaxSubKeyLength, dwDeviceLength, i; // // Open the enumerator key, under HKLM\SYSTEM\CCS\Enum. // result = RegOpenKeyEx(CCSEnumKey, Enumerator, 0, KEY_READ, &hEnumeratorKey); if (result != ERROR_SUCCESS) { // // If we failed to open the Enumerator key, there's nothing we can do. // DBGTRACE( DBGF_ERRORS, (TEXT("EnumerateDeviceKeys: failed to open '%s' enumerator key, error=0x%08lx\n"), Enumerator, result)); goto Clean0; } // // Query the enumerator key for device subkey information. // result = RegQueryInfoKey(hEnumeratorKey, NULL, NULL, NULL, &dwSubkeyCount, &dwMaxSubKeyLength, NULL, NULL, NULL, NULL, NULL, NULL); if (result != ERROR_SUCCESS) { DBGTRACE( DBGF_ERRORS, (TEXT("EnumerateDeviceKeys: failed to query '%s' enumerator key, error=0x%08lx\n"), Enumerator, result)); goto Clean0; } // // Allocate a buffer to hold the largest device subkey name. // dwMaxSubKeyLength++; pszDeviceName = MyMalloc(dwMaxSubKeyLength * sizeof(TCHAR)); if (!pszDeviceName) { DBGTRACE( DBGF_ERRORS, (TEXT("EnumerateDeviceKeys: failed to allocate buffer for device subkeys of '%s'\n"), Enumerator)); result = ERROR_NOT_ENOUGH_MEMORY; goto Clean0; } // // Enumerate the enumerator's devices. // for (i = 0; i < dwSubkeyCount; i++) { dwDeviceLength = dwMaxSubKeyLength; result = RegEnumKeyEx(hEnumeratorKey, i, pszDeviceName, &dwDeviceLength, 0, NULL, NULL, NULL); if (result != ERROR_SUCCESS) { // // If there was some error enumerating this device key, skip it. // MYASSERT(result != ERROR_NO_MORE_ITEMS); DBGTRACE( DBGF_WARNINGS, (TEXT("EnumerateDeviceKeys: failed to enumerate device subkey for '%s', error=0x%08lx\n"), Enumerator, result)); result = ERROR_SUCCESS; continue; } // // Enumerate the device instances, and append the migration data for // each to the section buffer. // if (!EnumerateInstanceKeys(hEnumeratorKey, Enumerator, pszDeviceName, pszDeviceInstanceSection, pszDeviceInstanceCurrent, dwDeviceInstanceSectionLength, dwDeviceInstanceSectionRemaining)) { DBGTRACE( DBGF_ERRORS, (TEXT("EnumerateDeviceKeys: EnumerateInstanceKeys failed for %s\\%s, error=0x%08lx\n"), Enumerator, pszDeviceName, GetLastError())); } } Clean0: // // Do some cleanup // if (pszDeviceName) { MyFree(pszDeviceName); } if (hEnumeratorKey) { RegCloseKey(hEnumeratorKey); } if (result != ERROR_SUCCESS) { SetLastError(result); } return (result == ERROR_SUCCESS); } // EnumerateDeviceKeys() BOOL EnumerateInstanceKeys( IN HKEY EnumeratorKey, IN LPTSTR Enumerator, IN LPTSTR Device, IN OUT LPTSTR *pszDeviceInstanceSection, IN OUT LPTSTR *pszDeviceInstanceCurrent, IN OUT DWORD *dwDeviceInstanceSectionLength, IN OUT DWORD *dwDeviceInstanceSectionRemaining ) /*++ Routine Description: Enumerates instance keys of a device. Worker routine for EnumerateDeviceKeys,MigrateDeviceInstanceData. Return Value: TRUE if successful, FALSE otherwise. Upon failure, additional information can be retrieved by calling GetLastError(). --*/ { LONG result = ERROR_SUCCESS; HKEY hDeviceKey = NULL; LPTSTR pszDeviceInstanceId = NULL; DWORD dwSubkeyCount, dwMaxSubKeyLength, dwSpaceNeeded, dwSpaceConsumed, i; BOOL bIsDeviceRootEnumerated; // // Keep track of whether this is a ROOT enumerated device. // bIsDeviceRootEnumerated = (lstrcmpi(Enumerator, REGSTR_KEY_ROOTENUM) == 0); // // If this is a LEGACY_ ROOT enumerated device, don't bother migrating it // for textmode setup to see. // if (bIsDeviceRootEnumerated) { if (wcsncmp(Device, TEXT("LEGACY_"), lstrlen(TEXT("LEGACY_"))) == 0) { return TRUE; } } // // Open the device key, under the enumerator key. // result = RegOpenKeyEx(EnumeratorKey, Device, 0, KEY_READ, &hDeviceKey); if (result != ERROR_SUCCESS) { DBGTRACE( DBGF_ERRORS, (TEXT("EnumerateInstanceKeys: failed to open '%s\\%s' device key, error=0x%08lx\n"), Enumerator, Device, result)); goto Clean0; } // // Query the device key for instance subkey information. // result = RegQueryInfoKey(hDeviceKey, NULL, NULL, NULL, &dwSubkeyCount, &dwMaxSubKeyLength, NULL, NULL, NULL, NULL, NULL, NULL); if (result != ERROR_SUCCESS) { DBGTRACE( DBGF_ERRORS, (TEXT("EnumerateInstanceKeys: failed to query '%s\\%s' device key, error=0x%08lx\n"), Enumerator, Device, result)); goto Clean0; } // // Allocate a buffer to hold the largest device instance subkey name. // dwMaxSubKeyLength++; pszDeviceInstanceId = MyMalloc(dwMaxSubKeyLength * sizeof(TCHAR)); if (!pszDeviceInstanceId) { DBGTRACE( DBGF_ERRORS, (TEXT("EnumerateInstanceKeys: failed to allocate buffer for instance subkeys of '%s\\%s'\n"), Enumerator, Device)); result = ERROR_NOT_ENOUGH_MEMORY; goto Clean0; } // // Enumerate the device's instances. // for (i = 0; i < dwSubkeyCount; i++) { DWORD dwInstanceLength, dwType, dwBufferSize; DWORD dwUniqueParentID, dwFirmwareIdentified; TCHAR szParentIdPrefix[MAX_PATH]; TCHAR szUniqueParentID[11], szFirmwareIdentified[11]; TCHAR szDriver[2*MAX_PATH + 1]; GUID classGuid; DWORD dwDrvInst; HKEY hInstanceKey = NULL, hLogConfKey = NULL; TCHAR szService[MAX_PATH]; PBYTE pBootConfig = NULL; LPTSTR pszBootConfig = NULL; DWORD dwBootConfigSize = 0; dwInstanceLength = dwMaxSubKeyLength; result = RegEnumKeyEx(hDeviceKey, i, pszDeviceInstanceId, &dwInstanceLength, 0, NULL, NULL, NULL); if (result != ERROR_SUCCESS) { // // If there was some error enumerating this key, skip it. // MYASSERT(result != ERROR_NO_MORE_ITEMS); DBGTRACE( DBGF_WARNINGS, (TEXT("EnumerateInstanceKeys: failed to enumerate instance subkey of '%s\\%s', error=0x&08lx\n"), Enumerator, Device, result)); result = ERROR_SUCCESS; continue; } result = RegOpenKeyEx(hDeviceKey, pszDeviceInstanceId, 0, KEY_READ, &hInstanceKey); if (result != ERROR_SUCCESS) { DBGTRACE( DBGF_WARNINGS, (TEXT("EnumerateInstanceKeys: failed to open '%s\\%s\\%s', error=0x%08lx\n"), Enumerator, Device, pszDeviceInstanceId, result)); result = ERROR_SUCCESS; continue; } // // Check for the "UniqueParentID" value // dwBufferSize = sizeof(dwUniqueParentID); result = RegQueryValueEx(hInstanceKey, REGSTR_VALUE_UNIQUE_PARENT_ID, 0, &dwType, (LPBYTE)&dwUniqueParentID, &dwBufferSize); if ((result == ERROR_SUCCESS) && (dwType == REG_DWORD)){ // // Write the UniqueParentID value to the sif as a base 16 value. // (see admin\ntsetup\textmode\kernel\spsetup.c) // wsprintf(szUniqueParentID, TEXT("%X"), // base 16 dwUniqueParentID); } else { // // No "UniqueParentID" value to migrate. // *szUniqueParentID = TEXT('\0'); } // // Check for the "ParentIdPrefix" value // dwBufferSize = sizeof(szParentIdPrefix); result = RegQueryValueEx(hInstanceKey, REGSTR_VALUE_PARENT_ID_PREFIX, 0, &dwType, (LPBYTE)szParentIdPrefix, &dwBufferSize); if ((result != ERROR_SUCCESS) || (dwType != REG_SZ)) { // // No "ParentIdPrefix" value to migrate. // *szParentIdPrefix = TEXT('\0'); result = ERROR_SUCCESS; } // // Build the device's Driver key name by checking for the GUID and // DrvInst values. // *szDriver = TEXT('\0'); dwBufferSize = sizeof(classGuid); result = RegQueryValueEx(hInstanceKey, TEXT("GUID"), 0, &dwType, (LPBYTE)&classGuid, &dwBufferSize); if ((result == ERROR_SUCCESS) && (dwType == REG_BINARY)) { // // Get the DrvInst value for the driver key // dwBufferSize = sizeof(dwDrvInst); result = RegQueryValueEx(hInstanceKey, TEXT("DrvInst"), 0, &dwType, (LPBYTE)&dwDrvInst, &dwBufferSize); if ((result == ERROR_SUCCESS) && (dwType == REG_DWORD)) { if (pSifUtilStringFromGuid(&classGuid, szDriver, sizeof(szDriver)/sizeof(TCHAR))) { // // Build the driver key // wsprintf((LPWSTR)szDriver, TEXT("%s\\%04u"), szDriver, dwDrvInst); } else { result = GetLastError(); } } else { // // Generic error value so we try to find the driver value using // the old scheme. // result = ERROR_INVALID_PARAMETER; } } else { // // Generic error value so we try to find the driver value using // the old scheme. // result = ERROR_INVALID_PARAMETER; } // // If this device instance key is not using the new GUID\DrvInst // scheme, check for the "Driver" value // if (result != ERROR_SUCCESS) { dwBufferSize = sizeof(szDriver); result = RegQueryValueEx(hInstanceKey, REGSTR_VAL_DRIVER, 0, &dwType, (LPBYTE)szDriver, &dwBufferSize); if ((result != ERROR_SUCCESS) || (dwType != REG_SZ)) { // // No "Driver" value to migrate. // *szDriver = TEXT('\0'); result = ERROR_SUCCESS; } } // // If this is a ROOT enumerated device, check for Service, BootConfig // and FirmwareIdentified values. // if (bIsDeviceRootEnumerated) { // // Check for the "Service" value. // dwBufferSize = sizeof(szService); result = RegQueryValueEx(hInstanceKey, REGSTR_VAL_SERVICE, 0, &dwType, (LPBYTE)szService, &dwBufferSize); if ((result != ERROR_SUCCESS) || (dwType != REG_SZ)) { // // No "Service" value to migrate. // *szService = TEXT('\0'); result = ERROR_SUCCESS; } // // Check for the "LogConf\BootConfig" value. // result = RegOpenKeyEx(hInstanceKey, REGSTR_KEY_LOGCONF, 0, KEY_READ, &hLogConfKey); if (result == ERROR_SUCCESS) { result = RegQueryValueEx(hLogConfKey, REGSTR_VAL_BOOTCONFIG, 0, &dwType, (LPBYTE)NULL, &dwBootConfigSize); if ((result == ERROR_SUCCESS) && (dwType == REG_RESOURCE_LIST)) { pBootConfig = MyMalloc(dwBootConfigSize); if (pBootConfig) { result = RegQueryValueEx(hLogConfKey, REGSTR_VAL_BOOTCONFIG, 0, &dwType, (LPBYTE)pBootConfig, &dwBootConfigSize); if ((result == ERROR_SUCCESS) && (dwType == REG_RESOURCE_LIST)) { // // Allocate a string buffer large enough to store // each nibble of the BootConfig data as a separate // character. // pszBootConfig = MyMalloc((dwBootConfigSize*2 + 1)*sizeof(TCHAR)); if (pszBootConfig) { DWORD b; // // Convert the binary BootConfig data to a string // format that we can throw into the sif. // for (b = 0; b < dwBootConfigSize; b++) { // first write the high-order nibble, wsprintf((PTCHAR)&pszBootConfig[2*b], TEXT("%X"), pBootConfig[b] / (0x10)); // then the low order nibble. wsprintf((PTCHAR)&pszBootConfig[2*b + 1], TEXT("%X"), pBootConfig[b] % (0x10)); } } } MyFree(pBootConfig); pBootConfig = NULL; } } else { // // No "LogConf\BootConfig" value to migrate. // pszBootConfig = NULL; } RegCloseKey(hLogConfKey); } // // Check for the "FirmwareIdentified" value // dwBufferSize = sizeof(dwFirmwareIdentified); result = RegQueryValueEx(hInstanceKey, REGSTR_VAL_FIRMWAREIDENTIFIED, 0, &dwType, (LPBYTE)&dwFirmwareIdentified, &dwBufferSize); if ((result == ERROR_SUCCESS) && (dwType == REG_DWORD)){ // // Write the FirmwareIdentified value to the sif as a base 16 value. // (see admin\ntsetup\textmode\kernel\spsetup.c) // wsprintf(szFirmwareIdentified, TEXT("%X"), // base 16 dwFirmwareIdentified); } else { // // No "FirmwareIdentified" value to migrate. // *szFirmwareIdentified = TEXT('\0'); } } else { // // We only migrate Service, BootConfig, and FirmwareIdentified // values for Root enumerated devices. // *szService = TEXT('\0'); pszBootConfig = NULL; *szFirmwareIdentified = TEXT('\0'); } // // If there are no values to migrate for this device instance, skip it. // if (!*szUniqueParentID && !*szDriver && !*szParentIdPrefix && !*szService && !pszBootConfig && !*szFirmwareIdentified) { continue; } // // If any of the strings cannot be migrated, skip it. // if ((!CanStringBeMigrated(szDriver)) || (!CanStringBeMigrated(szService)) || (!CanStringBeMigrated(Enumerator)) || (!CanStringBeMigrated(Device)) || (!CanStringBeMigrated(pszDeviceInstanceId))) { continue; } // // This block appends the class key data we want to migrate to a // multi-sz style string that will be written to the sif file. // // // Need space in the section buffer for a string of the form: // Enumerator\Device\Instance,UniqueParentID,ParentIdPrefix,DriverKey,Service,BootConfig // // // First, determine the space required by the common parts. // dwSpaceNeeded = 1 + // TEXT('\"') lstrlen(Enumerator) + 1 + // TEXT('\\') lstrlen(Device) + 1 + // TEXT('\\') lstrlen(pszDeviceInstanceId) + 1 + // TEXT('\"') 1; // TEXT(',') // // Next, determine the space required, based on the data we have. // if (*szFirmwareIdentified) { dwSpaceNeeded += lstrlen(szUniqueParentID) + 1 + // TEXT(',') lstrlen(szParentIdPrefix) + 1 + // TEXT(',') lstrlen(szDriver) + 1 + // TEXT(',') 1 + // TEXT('"') lstrlen(szService) + 1 + // TEXT('"') 1 + // TEXT(',') (pszBootConfig ? lstrlen(pszBootConfig) : 0) + 1 + // TEXT(',') lstrlen(szFirmwareIdentified); } else if (pszBootConfig) { dwSpaceNeeded += lstrlen(szUniqueParentID) + 1 + // TEXT(',') lstrlen(szParentIdPrefix) + 1 + // TEXT(',') lstrlen(szDriver) + 1 + // TEXT(',') 1 + // TEXT('"') lstrlen(szService) + 1 + // TEXT('"') 1 + // TEXT(',') lstrlen(pszBootConfig); } else if (*szService) { dwSpaceNeeded += lstrlen(szUniqueParentID) + 1 + // TEXT(',') lstrlen(szParentIdPrefix) + 1 + // TEXT(',') lstrlen(szDriver) + 1 + // TEXT(',') 1 + // TEXT('"') lstrlen(szService) + 1; // TEXT('"') } else if (*szDriver) { dwSpaceNeeded += lstrlen(szUniqueParentID) + 1 + // TEXT(',') lstrlen(szParentIdPrefix) + 1 + // TEXT(',') lstrlen(szDriver); } else if (*szParentIdPrefix) { dwSpaceNeeded += lstrlen(szUniqueParentID) + 1 + // TEXT(',') lstrlen(szParentIdPrefix); } else if (*szUniqueParentID) { dwSpaceNeeded += lstrlen(szUniqueParentID); } // // Account for the NULL terminator. // dwSpaceNeeded += 1; if (*dwDeviceInstanceSectionRemaining <= dwSpaceNeeded) { // // ReAllocate the section block. // LPTSTR p; DWORD dwTempSectionLength, dwTempSectionRemaining; dwTempSectionRemaining = *dwDeviceInstanceSectionRemaining + *dwDeviceInstanceSectionLength; dwTempSectionLength = *dwDeviceInstanceSectionLength * 2; p = *pszDeviceInstanceSection; p = MyRealloc(p, dwTempSectionLength*sizeof(TCHAR)); if (!p) { DBGTRACE( DBGF_ERRORS, (TEXT("EnumerateInstanceKeys: REALLOC failed!!!\n") )); result = ERROR_NOT_ENOUGH_MEMORY; RegCloseKey(hInstanceKey); goto Clean0; } *pszDeviceInstanceSection = p; *dwDeviceInstanceSectionRemaining = dwTempSectionRemaining; *dwDeviceInstanceSectionLength = dwTempSectionLength; *pszDeviceInstanceCurrent = *pszDeviceInstanceSection + (*dwDeviceInstanceSectionLength - *dwDeviceInstanceSectionRemaining); } // // Write the current line to the section block. // if (*szFirmwareIdentified) { dwSpaceConsumed = wsprintf(*pszDeviceInstanceCurrent, TEXT("\"%s\\%s\\%s\",%s,%s,%s,\"%s\",%s,%s"), Enumerator, Device, pszDeviceInstanceId, szUniqueParentID, szParentIdPrefix, szDriver, szService, (pszBootConfig ? pszBootConfig : TEXT("\0")), szFirmwareIdentified); } else if (pszBootConfig) { dwSpaceConsumed = wsprintf(*pszDeviceInstanceCurrent, TEXT("\"%s\\%s\\%s\",%s,%s,%s,\"%s\",%s"), Enumerator, Device, pszDeviceInstanceId, szUniqueParentID, szParentIdPrefix, szDriver, szService, pszBootConfig); } else if (*szService) { dwSpaceConsumed = wsprintf(*pszDeviceInstanceCurrent, TEXT("\"%s\\%s\\%s\",%s,%s,%s,\"%s\""), Enumerator, Device, pszDeviceInstanceId, szUniqueParentID, szParentIdPrefix, szDriver, szService); } else if (*szDriver) { dwSpaceConsumed = wsprintf(*pszDeviceInstanceCurrent, TEXT("\"%s\\%s\\%s\",%s,%s,%s"), Enumerator, Device, pszDeviceInstanceId, szUniqueParentID, szParentIdPrefix, szDriver); } else if (*szParentIdPrefix) { dwSpaceConsumed = wsprintf(*pszDeviceInstanceCurrent, TEXT("\"%s\\%s\\%s\",%s,%s"), Enumerator, Device, pszDeviceInstanceId, szUniqueParentID, szParentIdPrefix); } else if (*szUniqueParentID) { dwSpaceConsumed = wsprintf(*pszDeviceInstanceCurrent, TEXT("\"%s\\%s\\%s\",%s"), Enumerator, Device, pszDeviceInstanceId, szUniqueParentID); } // // Free the allocated BootConfig string buffer. // if (pszBootConfig) { MyFree(pszBootConfig); pszBootConfig = NULL; } // // Account for the NULL terminator // dwSpaceConsumed += 1; *pszDeviceInstanceCurrent += dwSpaceConsumed; *dwDeviceInstanceSectionRemaining -= dwSpaceConsumed; // // Close the device instance key // RegCloseKey(hInstanceKey); } Clean0: // // Do some cleanup // if (pszDeviceInstanceId) { MyFree(pszDeviceInstanceId); } if (hDeviceKey != NULL) { RegCloseKey(hDeviceKey); } if (result != ERROR_SUCCESS) { SetLastError(result); } return (result == ERROR_SUCCESS); } // EnumerateInstanceKeys() BOOL CanStringBeMigrated( IN LPTSTR pszBuffer ) { LPTSTR p; BOOL bStatus; try { // // An empty string can be migrated. // if (!ARGUMENT_PRESENT(pszBuffer)) { bStatus = TRUE; goto Clean0; } for (p = pszBuffer; *p; p++) { // // Check for the presence of non-migratable characters. // if ((*p == TEXT('=')) || (*p == TEXT('"'))) { bStatus = FALSE; goto Clean0; } } // // Found no problems with the string. // bStatus = TRUE; Clean0: NOTHING; } except(EXCEPTION_EXECUTE_HANDLER) { bStatus = FALSE; } return bStatus; } // CanStringBeMigrated // // Class key enumeration routines // BOOL MigrateClassKeys( OUT LPTSTR *Buffer ) /*++ Routine Description: This routine walks the Plug and Play setup class branch of the registry, and collects the data about what keys currently exist. This information is relevant to maintaining plug and play state during textmode setup, such that the names of existing keys are not reassigned before they have been migrated to the registry at the end of textmode setup. Specifically, a multi-sz string will be returned to the caller that contains each subkey of the class branch. Arguments: Buffer - Supplies the address of a character pointer, that on success will contain a multi-sz list of setup class subkeys to migrate. The caller is responsible for freeing the memory via LocalFree. Return Value: TRUE if successful, FALSE otherwise. Upon failure, additional information can be retrieved by calling GetLastError(). --*/ { LONG result = ERROR_SUCCESS; HKEY hClassKey = NULL; DWORD dwSubkeyCount, dwMaxSubKeyLength, i; LPTSTR pszClassKeyName = NULL; LPTSTR pszClassKeySection = NULL; LPTSTR pszClassKeyCurrent = NULL; DWORD dwClassKeySectionLength = 0; DWORD dwClassKeySectionRemaining = 0; // // Initialize the output parameter. // *Buffer = NULL; // // Allocate storage and initialize variables for the class Key migration // section. // if (pszClassKeySection == NULL) { dwClassKeySectionLength = dwClassKeySectionRemaining = 256; pszClassKeySection = MyMalloc(dwClassKeySectionLength * sizeof(TCHAR)); if (!pszClassKeySection) { DBGTRACE( DBGF_ERRORS, (TEXT("MigrateClassKeys: initial ALLOC for ClassKeySection failed!!\n") )); result = ERROR_NOT_ENOUGH_MEMORY; goto Clean0; } pszClassKeyCurrent = pszClassKeySection; } // // Open a handle to the HKLM\SYSTEM\CCS\Control\Class key. // result = RegOpenKeyEx(HKEY_LOCAL_MACHINE, REGSTR_PATH_CLASS_NT, 0, KEY_READ, &hClassKey); if (result != ERROR_SUCCESS) { DBGTRACE( DBGF_ERRORS, (TEXT("MigrateClassKeys: failed to open %s, error=0x%08lx\n"), REGSTR_PATH_CLASS_NT, result)); goto Clean0; } // // Query the Class key for class GUID subkey information. // result = RegQueryInfoKey(hClassKey, NULL, NULL, NULL, &dwSubkeyCount, &dwMaxSubKeyLength, NULL, NULL, NULL, NULL, NULL, NULL); if (result != ERROR_SUCCESS) { DBGTRACE( DBGF_ERRORS, (TEXT("MigrateClassKeys: failed to query %s key, error=0x%08lx\n"), REGSTR_PATH_CLASS_NT, result)); goto Clean0; } // // Allocate a buffer to hold the largest setup class GUID subkey name. // dwMaxSubKeyLength++; MYASSERT(dwMaxSubKeyLength == MAX_GUID_STRING_LEN); pszClassKeyName = MyMalloc(dwMaxSubKeyLength * sizeof(TCHAR)); if (!pszClassKeyName) { result = ERROR_NOT_ENOUGH_MEMORY; DBGTRACE( DBGF_ERRORS, (TEXT("MigrateClassKeys: ALLOC for Class GUID key names failed!!\n") )); goto Clean0; } // // Enumerate the setup class GUIDs. // for (i = 0; i < dwSubkeyCount; i++) { DWORD dwClassKeyLength; dwClassKeyLength = dwMaxSubKeyLength; result = RegEnumKeyEx(hClassKey, i, pszClassKeyName, &dwClassKeyLength, 0, NULL, NULL, NULL); if (result != ERROR_SUCCESS) { // // If there was some error enumerating this key, skip it. // MYASSERT(result != ERROR_NO_MORE_ITEMS); DBGTRACE( DBGF_WARNINGS, (TEXT("MigrateClassKeys: failed to enumerate a class subkey, error=0x%08lx\n"), result)); result = ERROR_SUCCESS; continue; } // // Enumerate all subkeys for a given setup class key, and append them to // the section buffer. // if (!EnumerateClassSubkeys(hClassKey, pszClassKeyName, &pszClassKeySection, &pszClassKeyCurrent, &dwClassKeySectionLength, &dwClassKeySectionRemaining)) { DBGTRACE( DBGF_ERRORS, (TEXT("EnumerateClassSubkeys failed, error=0x%08lx\n"), GetLastError())); } } // // Once we've enumerated all class subkeys, add the final NULL terminator to // the multi-sz buffer. There must be enough space for the final NULL // terminator because the buffer is always reallocated unless there is room. // MYASSERT(dwClassKeySectionRemaining > 0); MYASSERT(pszClassKeyCurrent); *pszClassKeyCurrent = TEXT('\0'); dwClassKeySectionRemaining -= 1; Clean0: // // Do some cleanup. // if (pszClassKeyName) { MyFree(pszClassKeyName); } if (hClassKey) { RegCloseKey(hClassKey); } // // Return the buffer to the caller only if successful. // if (result == ERROR_SUCCESS) { *Buffer = pszClassKeySection; } else { SetLastError(result); if (pszClassKeySection) { MyFree(pszClassKeySection); } } return (result == ERROR_SUCCESS); } // MigrateClassKeys() BOOL EnumerateClassSubkeys( IN HKEY ClassKey, IN LPTSTR ClassKeyName, IN OUT LPTSTR *pszClassKeySection, IN OUT LPTSTR *pszClassKeyCurrent, IN OUT DWORD *dwClassKeySectionLength, IN OUT DWORD *dwClassKeySectionRemaining ) /*++ Routine Description: Enumerates subkeys of a setup class key. Worker routine for MigrateClassKeys. Return Value: TRUE if successful, FALSE otherwise. Upon failure, additional information can be retrieved by calling GetLastError(). --*/ { LONG result = ERROR_SUCCESS; HKEY hClassSubkey = NULL; LPTSTR pszClassSubkey = NULL; DWORD dwSubkeyCount, dwMaxSubKeyLength, dwSpaceNeeded, dwSpaceConsumed, i; // // Open the class subkey. // result = RegOpenKeyEx(ClassKey, ClassKeyName, 0, KEY_READ, &hClassSubkey); if (result != ERROR_SUCCESS) { DBGTRACE( DBGF_ERRORS, (TEXT("EnumerateClassSubkeys: failed to open '%s' class key, error=0x%08lx\n"), ClassKeyName, result)); goto Clean0; } // // Query the class GUID key for setup class subkey information. // result = RegQueryInfoKey(hClassSubkey, NULL, NULL, NULL, &dwSubkeyCount, &dwMaxSubKeyLength, NULL, NULL, NULL, NULL, NULL, NULL); if (result != ERROR_SUCCESS) { DBGTRACE( DBGF_ERRORS, (TEXT("EnumerateClassSubkeys: failed to query '%s' class key, error=0x%08lx\n"), ClassKeyName, result)); goto Clean0; } // // Allocate a buffer to hold the largest setup class subkey name. // dwMaxSubKeyLength++; pszClassSubkey = MyMalloc(dwMaxSubKeyLength * sizeof(TCHAR)); if (!pszClassSubkey) { result = ERROR_NOT_ENOUGH_MEMORY; DBGTRACE( DBGF_ERRORS, (TEXT("EnumerateClassSubkeys: ALLOC for Class GUID subkey names failed!!\n") )); goto Clean0; } // // Enumerate the setup class's "software" subkeys. // for (i = 0; i < dwSubkeyCount; i++) { DWORD dwClassSubkeyLength; dwClassSubkeyLength = dwMaxSubKeyLength; result = RegEnumKeyEx(hClassSubkey, i, pszClassSubkey, &dwClassSubkeyLength, 0, NULL, NULL, NULL); if ((result != ERROR_SUCCESS) || (dwClassSubkeyLength != 4)) { // // if there was some error, or this is not an actual "software" key // (in the form "XXXX"), skip this key and move on. // if (result != ERROR_SUCCESS) { MYASSERT(result != ERROR_NO_MORE_ITEMS); DBGTRACE( DBGF_WARNINGS, (TEXT("EnumerateClassSubkeys: failed to enumerate a '%s' subkey, error=0x%08lx\n"), ClassKeyName, result)); } result = ERROR_SUCCESS; continue; } // // This block appends the class key data we want to migrate to a // multi-sz style string that will be written to the sif file. // // // Need space in the section buffer for a string of the form: // ClassKeyName\pszClassSubkey // dwSpaceNeeded = lstrlen(ClassKeyName) + 1 + // TEXT('\\') lstrlen(pszClassSubkey); // // Account for the NULL terminator. // dwSpaceNeeded += 1; if (*dwClassKeySectionRemaining <= dwSpaceNeeded) { // // ReAllocate the section block. // LPTSTR p; DWORD dwTempSectionLength, dwTempSectionRemaining; dwTempSectionRemaining = *dwClassKeySectionRemaining + *dwClassKeySectionLength; dwTempSectionLength = *dwClassKeySectionLength * 2; p = *pszClassKeySection; p = MyRealloc(p, dwTempSectionLength*sizeof(TCHAR)); if (!p) { DBGTRACE( DBGF_ERRORS, (TEXT("EnumerateClassSubkeys: REALLOC failed!!!\n") )); result = ERROR_NOT_ENOUGH_MEMORY; goto Clean0; } *pszClassKeySection = p; *dwClassKeySectionRemaining = dwTempSectionRemaining; *dwClassKeySectionLength = dwTempSectionLength; *pszClassKeyCurrent = *pszClassKeySection + (*dwClassKeySectionLength - *dwClassKeySectionRemaining); } // // Write the current line to the section block. // dwSpaceConsumed = wsprintf(*pszClassKeyCurrent, TEXT("%s\\%s"), ClassKeyName, pszClassSubkey); // // Account for the NULL terminator. // dwSpaceConsumed += 1; *pszClassKeyCurrent += dwSpaceConsumed; *dwClassKeySectionRemaining -= dwSpaceConsumed; } Clean0: // // Do some cleanup. // if (hClassSubkey != NULL) { RegCloseKey(hClassSubkey); } if (pszClassSubkey) { MyFree(pszClassSubkey); } if (result != ERROR_SUCCESS) { SetLastError(result); } return (result == ERROR_SUCCESS); } // EnumerateClassSubkeys() // // Hash value migration routines // BOOL MigrateHashValues( OUT LPTSTR *Buffer ) /*++ Routine Description: This routine searches the Plug and Play Enum key of the registry, and collects the data about what hash value entries currently exist. This information is relevant to maintaining plug and play state during textmode setup, such that the names of existing device instances are not reassigned before they have been migrated to the registry at the end of textmode setup. Specifically, a multi-sz string will be returned to the caller that contains the name of the hash value, and its count. Arguments: Buffer - Supplies the address of a character pointer, that on success will contain a multi-sz list of hash values to migrate. The caller is responsible for freeing the memory via LocalFree. Return Value: TRUE if successful, FALSE otherwise. Upon failure, additional information can be retrieved by calling GetLastError(). --*/ { LONG result = ERROR_SUCCESS; HKEY hEnumKey = NULL; DWORD dwValueCount, dwMaxValueNameLength, dwSpaceNeeded, dwSpaceConsumed, i; LPTSTR pszHashValueName = NULL; LPTSTR pszHashValueSection = NULL; LPTSTR pszHashValueCurrent = NULL; DWORD dwHashValueSectionLength = 0; DWORD dwHashValueSectionRemaining = 0; // // Initialize the output parameter. // *Buffer = NULL; #if DO_LOCK_UNLOCK // DO_LOCK_UNLOCK // // Unlock the Enum key // LockUnlockEnumTree(FALSE); #endif // DO_LOCK_UNLOCK // // Allocate storage and initialize variables for the hash value migration // section. // if (pszHashValueSection == NULL) { dwHashValueSectionLength = dwHashValueSectionRemaining = 256; pszHashValueSection = MyMalloc(dwHashValueSectionLength * sizeof(TCHAR)); if (!pszHashValueSection) { result = ERROR_NOT_ENOUGH_MEMORY; DBGTRACE( DBGF_ERRORS, (TEXT("MigrateHashValues: initial ALLOC for HashValueSection failed!!\n") )); goto Clean0; } pszHashValueCurrent = pszHashValueSection; } // // Open a handle to the HKLM\SYSTEM\CCS\Enum key. // result = RegOpenKeyEx(HKEY_LOCAL_MACHINE, REGSTR_PATH_SYSTEMENUM, 0, KEY_READ, &hEnumKey); if (result != ERROR_SUCCESS) { DBGTRACE( DBGF_ERRORS, (TEXT("MigrateHashValues: failed to open %s, error=0x%08lx\n"), REGSTR_PATH_SYSTEMENUM, result)); goto Clean0; } // // Query the Enum key for hash value information. // result = RegQueryInfoKey(hEnumKey, NULL, NULL, NULL, NULL, NULL, NULL, &dwValueCount, &dwMaxValueNameLength, NULL, NULL, NULL); if (result != ERROR_SUCCESS) { DBGTRACE( DBGF_ERRORS, (TEXT("MigrateHashValues: failed to query %s key, error=0x%08lx\n"), REGSTR_PATH_SYSTEMENUM, result)); goto Clean0; } // // Allocate a variable to hold the largest hash value key name. // dwMaxValueNameLength++; pszHashValueName = MyMalloc(dwMaxValueNameLength * sizeof(TCHAR)); if (!pszHashValueName) { DBGTRACE( DBGF_ERRORS, (TEXT("MigrateHashValues: failed to allocate buffer for Enum key hash values\n") )); result = ERROR_NOT_ENOUGH_MEMORY; goto Clean0; } // // Enumerate all values and append them to the supplied buffer. // for (i = 0; i < dwValueCount; i++) { DWORD dwHashValueLength, dwType, dwData, dwSize; TCHAR szHashValueData[11]; dwHashValueLength = dwMaxValueNameLength; dwType = REG_DWORD; dwData = 0; dwSize = sizeof(DWORD); result = RegEnumValue(hEnumKey, i, pszHashValueName, &dwHashValueLength, 0, &dwType, (LPBYTE)&dwData, &dwSize); if ((result != ERROR_SUCCESS) || (dwType != REG_DWORD) || (dwSize != sizeof(DWORD))) { // // If there was some error enumerating this value, or the value // return was not expected, skip it. // MYASSERT(result != ERROR_NO_MORE_ITEMS); DBGTRACE( DBGF_WARNINGS, (TEXT("MigrateHashValues: failed to enumerate Enum values, error=0x%08lx\n"), result)); result = ERROR_SUCCESS; continue; } // // Write the hash value data to the sif as a base 10 value. // (see admin\ntsetup\textmode\kernel\spsetup.c) // wsprintf(szHashValueData, TEXT("%d"), dwData); // base 10 // // This block appends the class key data we want to migrate to a // multi-sz style string that will be written to the sif file. // // // Need space in the section buffer for a string of the form: // HashValueName=HashValueData // dwSpaceNeeded = lstrlen(pszHashValueName) + 1 + // TEXT('=') lstrlen(szHashValueData); // // Account for the NULL terminator. // dwSpaceNeeded += 1; if (dwHashValueSectionRemaining <= dwSpaceNeeded) { // // ReAllocate the section block. // LPTSTR p; DWORD dwTempSectionLength, dwTempSectionRemaining; dwTempSectionRemaining = dwHashValueSectionRemaining + dwHashValueSectionLength; dwTempSectionLength = dwHashValueSectionLength * 2; p = pszHashValueSection; p = MyRealloc(p, dwTempSectionLength*sizeof(TCHAR)); if (!p) { DBGTRACE( DBGF_ERRORS, (TEXT("MigrateHashValues: REALLOC failed!!!\n") )); result = ERROR_NOT_ENOUGH_MEMORY; goto Clean0; } pszHashValueSection = p; dwHashValueSectionRemaining = dwTempSectionRemaining; dwHashValueSectionLength = dwTempSectionLength; pszHashValueCurrent = pszHashValueSection + (dwHashValueSectionLength - dwHashValueSectionRemaining); } // // Write the current line to the section block. // dwSpaceConsumed = wsprintf(pszHashValueCurrent, TEXT("%s=%s"), pszHashValueName, szHashValueData); // // Account for the NULL terminator. // dwSpaceConsumed += 1; pszHashValueCurrent += dwSpaceConsumed; dwHashValueSectionRemaining -= dwSpaceConsumed; } // // Once we've enumerated all hash values, add the final NULL terminator to // the multi-sz buffer. There must be enough space for the final NULL // terminator because the buffer is always reallocated unless there is room. // MYASSERT(dwHashValueSectionRemaining > 0); MYASSERT(pszHashValueCurrent); *pszHashValueCurrent = TEXT('\0'); dwHashValueSectionRemaining -= 1; Clean0: // // Do some cleanup // if (pszHashValueName) { MyFree(pszHashValueName); } if (hEnumKey) { RegCloseKey(hEnumKey); } #if DO_LOCK_UNLOCK // DO_LOCK_UNLOCK // // Lock the Enum tree // LockUnlockEnumTree(TRUE); #endif // DO_LOCK_UNLOCK // // Return the buffer to the caller only if successful. // if (result == ERROR_SUCCESS) { *Buffer = pszHashValueSection; } else { SetLastError(result); if (pszHashValueSection) { MyFree(pszHashValueSection); } } return (result == ERROR_SUCCESS); } // MigrateHashValues() // // Enum branch lock/unlock and security routines - taken from PNPREG. // (we only need these if we're doing the Enum lock/unlock thing) // #if DO_LOCK_UNLOCK // DO_LOCK_UNLOCK VOID LockUnlockEnumTree( IN BOOL bLock ) /*++ Routine Description: This function "locks" or "unlocks" the Plug and Play Enum tree in the registry. Arguments: bLock - If TRUE, specifies that the Enum tree should be "locked". Otherwise, specifies that the Enum tree should be "unlocked". Return Value: None. --*/ { PSECURITY_DESCRIPTOR pSD; HKEY hParentKey; LONG RegStatus; if (CreateSecurityDescriptors()) { EnumKeysAndApplyDacls(HKEY_LOCAL_MACHINE, REGSTR_PATH_SYSTEMENUM, 0, FALSE, !bLock, bLock ? &g_LockedPrivateKeysSD : &g_DeviceParametersSD, &g_DeviceParametersSD); FreeSecurityDescriptors(); } return; } // LockUnlockEnumTree() VOID EnumKeysAndApplyDacls( IN HKEY hParentKey, IN LPTSTR pszKeyName, IN DWORD dwLevel, IN BOOL bInDeviceParameters, IN BOOL bApplyTopDown, IN PSECURITY_DESCRIPTOR pPrivateKeySD, IN PSECURITY_DESCRIPTOR pDeviceParametersSD ) /*++ Routine Description: This function applies the DACL in pSD to all the keys rooted at hKey including hKey itself. Arguments: hParentKey - Handle to a registry key. pszKeyName - Name of the key. dwLevel - Number of levels remaining to recurse. pSD - Pointer to a security descriptor containing a DACL. Return Value: None. --*/ { LONG regStatus; DWORD dwMaxSubKeySize; LPTSTR pszSubKey; DWORD index; HKEY hKey; BOOL bNewInDeviceParameters; #if 0 //#if DBG // DBG DWORD dwStartKeyNameLength = g_dwCurrentKeyNameLength; if (g_dwCurrentKeyNameLength != 0) { g_szCurrentKeyName[ g_dwCurrentKeyNameLength++ ] = TEXT('\\'); } _tcscpy(&g_szCurrentKeyName[g_dwCurrentKeyNameLength], pszKeyName); g_dwCurrentKeyNameLength += _tcslen(pszKeyName); #endif // DBG DBGTRACE( DBGF_REGISTRY, (TEXT("EnumKeysAndApplyDacls(0x%08X, \"%s\", %d, %s, %s, 0x%08X, 0x%08X)\n"), hParentKey, g_szCurrentKeyName, dwLevel, bInDeviceParameters ? TEXT("TRUE") : TEXT("FALSE"), bApplyTopDown ? TEXT("TRUE") : TEXT("FALSE"), pPrivateKeySD, pDeviceParametersSD) ); if (bApplyTopDown) { regStatus = RegOpenKeyEx( hParentKey, pszKeyName, 0, WRITE_DAC, &hKey ); if (regStatus != ERROR_SUCCESS) { DBGTRACE( DBGF_ERRORS, (TEXT("EnumKeysAndApplyDacls(\"%s\") RegOpenKeyEx() failed, error = %d\n"), g_szCurrentKeyName, regStatus)); return; } DBGTRACE( DBGF_REGISTRY, (TEXT("Setting security on %s on the way down\n"), g_szCurrentKeyName) ); // // apply the new security to the registry key // regStatus = RegSetKeySecurity( hKey, DACL_SECURITY_INFORMATION, bInDeviceParameters ? pDeviceParametersSD : pPrivateKeySD ); if (regStatus != ERROR_SUCCESS) { DBGTRACE( DBGF_ERRORS, (TEXT("EnumKeysAndApplyDacls(\"%s\") RegSetKeySecurity() failed, error = %d\n"), g_szCurrentKeyName, regStatus)); } // // Close the key and reopen it later for read (which hopefully was just // granted in the DACL we just wrote // RegCloseKey( hKey ); } regStatus = RegOpenKeyEx( hParentKey, pszKeyName, 0, KEY_READ | WRITE_DAC, &hKey ); if (regStatus != ERROR_SUCCESS) { DBGTRACE( DBGF_ERRORS, (TEXT("EnumKeysAndApplyDacls(\"%s\") RegOpenKeyEx() failed, error = %d\n"), g_szCurrentKeyName, regStatus)); return; } // // Determine length of longest subkey // regStatus = RegQueryInfoKey( hKey, NULL, NULL, NULL, NULL, &dwMaxSubKeySize, NULL, NULL, NULL, NULL, NULL, NULL ); if (regStatus == ERROR_SUCCESS) { // // Allocate a buffer to hold the subkey names. RegQueryInfoKey returns the // size in characters and doesn't include the NUL terminator. // pszSubKey = LocalAlloc(0, ++dwMaxSubKeySize * sizeof(TCHAR)); if (pszSubKey != NULL) { // // Enumerate all the subkeys and then call ourselves recursively for each // until dwLevel reaches 0. // for (index = 0; ; index++) { regStatus = RegEnumKey( hKey, index, pszSubKey, dwMaxSubKeySize ); if (regStatus != ERROR_SUCCESS) { if (regStatus != ERROR_NO_MORE_ITEMS) { DBGTRACE( DBGF_ERRORS, (TEXT("EnumKeysAndApplyDacls(\"%s\") RegEnumKeyEx() failed, error = %d\n"), g_szCurrentKeyName, regStatus) ); } break; } bNewInDeviceParameters = bInDeviceParameters || (dwLevel == 3 && _tcsicmp( pszSubKey, REGSTR_KEY_DEVICEPARAMETERS ) == 0); EnumKeysAndApplyDacls( hKey, pszSubKey, dwLevel + 1, bNewInDeviceParameters, bApplyTopDown, pPrivateKeySD, pDeviceParametersSD ); } LocalFree( pszSubKey ); } } else { DBGTRACE( DBGF_ERRORS, (TEXT("EnumKeysAndApplyDacls(\"%s\") RegQueryInfoKey() failed, error = %d\n"), g_szCurrentKeyName, regStatus)); } if (!bApplyTopDown) { DBGTRACE( DBGF_REGISTRY, (TEXT("Setting security on %s on the way back up\n"), g_szCurrentKeyName) ); // // apply the new security to the registry key // regStatus = RegSetKeySecurity( hKey, DACL_SECURITY_INFORMATION, bInDeviceParameters ? pDeviceParametersSD : pPrivateKeySD ); if (regStatus != ERROR_SUCCESS) { DBGTRACE( DBGF_ERRORS, (TEXT("EnumKeysAndApplyDacls(\"%s\") RegSetKeySecurity() failed, error = %d\n"), g_szCurrentKeyName, regStatus)); } } RegCloseKey( hKey ); #if 0 //#if DBG // DBG g_dwCurrentKeyNameLength = dwStartKeyNameLength; g_szCurrentKeyName[g_dwCurrentKeyNameLength] = TEXT('\0'); #endif // DBG return; } // EnumKeysAndApplyDacls() BOOL CreateSecurityDescriptors( VOID ) /*++ Routine Description: This function creates a properly initialized Security Descriptor for the Device Parameters key and its subkeys. The SIDs and DACL created by this routine must be freed by calling FreeSecurityDescriptors. Arguments: None. Return Value: Pointer to the initialized Security Descriptor. NULL is returned if an error occurs. --*/ { SID_IDENTIFIER_AUTHORITY NtAuthority = SECURITY_NT_AUTHORITY; SID_IDENTIFIER_AUTHORITY WorldAuthority = SECURITY_WORLD_SID_AUTHORITY; EXPLICIT_ACCESS ExplicitAccess[3]; DWORD dwError; BOOL bSuccess; DWORD i; FARPROC pSetEntriesInAcl; HMODULE pAdvApi32; pAdvApi32 = LoadLibrary( TEXT("advapi32.dll")); if (!pAdvApi32) { return(FALSE); } #ifdef UNICODE pSetEntriesInAcl = GetProcAddress( pAdvApi32, "SetEntriesInAclW" ); #else pSetEntriesInAcl = GetProcAddress( pAdvApi32, "SetEntriesInAclA" ); #endif if (!pSetEntriesInAcl) { FreeLibrary( pAdvApi32 ); return(FALSE); } // // Create SIDs - Admins and System // bSuccess = AllocateAndInitializeSid( &NtAuthority, 2, SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_ADMINS, 0, 0, 0, 0, 0, 0, &g_pAdminSid); bSuccess = bSuccess && AllocateAndInitializeSid( &NtAuthority, 1, SECURITY_LOCAL_SYSTEM_RID, 0, 0, 0, 0, 0, 0, 0, &g_pSystemSid); bSuccess = bSuccess && AllocateAndInitializeSid( &WorldAuthority, 1, SECURITY_WORLD_RID, 0, 0, 0, 0, 0, 0, 0, &g_pWorldSid); if (bSuccess) { // // Initialize Access structures describing the ACEs we want: // System Full Control // Admins Full Control // // We'll take advantage of the fact that the unlocked private keys is // the same as the device parameters key and they are a superset of the // locked private keys. // // When we create the DACL for the private key we'll specify a subset of // the ExplicitAccess array. // for (i = 0; i < 3; i++) { ExplicitAccess[i].grfAccessMode = SET_ACCESS; ExplicitAccess[i].grfInheritance = CONTAINER_INHERIT_ACE; ExplicitAccess[i].Trustee.pMultipleTrustee = NULL; ExplicitAccess[i].Trustee.MultipleTrusteeOperation = NO_MULTIPLE_TRUSTEE; ExplicitAccess[i].Trustee.TrusteeForm = TRUSTEE_IS_SID; ExplicitAccess[i].Trustee.TrusteeType = TRUSTEE_IS_GROUP; } ExplicitAccess[0].grfAccessPermissions = KEY_ALL_ACCESS; ExplicitAccess[0].Trustee.ptstrName = (LPTSTR)g_pAdminSid; ExplicitAccess[1].grfAccessPermissions = KEY_ALL_ACCESS; ExplicitAccess[1].Trustee.ptstrName = (LPTSTR)g_pSystemSid; ExplicitAccess[2].grfAccessPermissions = KEY_READ; ExplicitAccess[2].Trustee.ptstrName = (LPTSTR)g_pWorldSid; // // Create the DACL with the both the above ACEs for the DeviceParameters // dwError = (DWORD)pSetEntriesInAcl( 3, ExplicitAccess, NULL, &g_pDeviceParametersDacl ); if (dwError == ERROR_SUCCESS) { // // Create the DACL with just the system ACE for the locked private // keys. // dwError = (DWORD)pSetEntriesInAcl( 2, ExplicitAccess + 1, NULL, &g_pLockedPrivateKeysDacl ); } bSuccess = dwError == ERROR_SUCCESS; } // // Initialize the DeviceParameters security descriptor // bSuccess = bSuccess && InitializeSecurityDescriptor( &g_DeviceParametersSD, SECURITY_DESCRIPTOR_REVISION ); // // Set the new DACL in the security descriptor // bSuccess = bSuccess && SetSecurityDescriptorDacl( &g_DeviceParametersSD, TRUE, g_pDeviceParametersDacl, FALSE); // // validate the new security descriptor // bSuccess = bSuccess && IsValidSecurityDescriptor( &g_DeviceParametersSD ); // // Initialize the DeviceParameters security descriptor // bSuccess = bSuccess && InitializeSecurityDescriptor( &g_LockedPrivateKeysSD, SECURITY_DESCRIPTOR_REVISION ); // // Set the new DACL in the security descriptor // bSuccess = bSuccess && SetSecurityDescriptorDacl( &g_LockedPrivateKeysSD, TRUE, g_pLockedPrivateKeysDacl, FALSE); // // validate the new security descriptor // bSuccess = bSuccess && IsValidSecurityDescriptor( &g_LockedPrivateKeysSD ); if (!bSuccess) { FreeSecurityDescriptors(); } FreeLibrary( pAdvApi32 ); return bSuccess; } // CreateSecurityDescriptors() VOID FreeSecurityDescriptors( VOID ) /*++ Routine Description: This function deallocates the data structures allocated and initialized by CreateSecurityDescriptors. Arguments: None. Return Value: None. --*/ { if (g_pDeviceParametersDacl) { LocalFree(g_pDeviceParametersDacl); g_pDeviceParametersDacl = NULL; } if (g_pLockedPrivateKeysDacl) { LocalFree(g_pLockedPrivateKeysDacl); g_pLockedPrivateKeysDacl = NULL; } if (g_pAdminSid != NULL) { FreeSid(g_pAdminSid); g_pAdminSid = NULL; } if (g_pSystemSid != NULL) { FreeSid(g_pSystemSid); g_pSystemSid = NULL; } if (g_pWorldSid != NULL) { FreeSid(g_pWorldSid); g_pWorldSid = NULL; } return; } // FreeSecurityDescriptors() #endif // DO_LOCK_UNLOCK