/*++ Copyright (c) 1998 Microsoft Corporation Module Name: pnpreg.c Abstract: The module contains the the initialization code for the executive component. It also contains the display string and shutdown system services. Author: Robert B. Nelson (robertn) 10-Feb-1998 Revision History: --*/ #include #include #include #include #include #include PSID g_pAdminSid; PSID g_pSystemSid; SECURITY_DESCRIPTOR g_DeviceParametersSD; PACL g_pDeviceParametersDacl; SECURITY_DESCRIPTOR g_LockedPrivateKeysSD; PACL g_pLockedPrivateKeysDacl; #if DBG || UMODETEST #define DBGF_ERRORS 0x00000001 #define DBGF_WARNINGS 0x00000002 #define DBGF_REGISTRY 0x00000010 void RegFixDebugMessage(LPTSTR format, ...); #define DBGTRACE(l, x) (g_RegFixDebugFlag & (l) ? RegFixDebugMessage x : (void)0) DWORD g_RegFixDebugFlag = DBGF_WARNINGS | DBGF_ERRORS; TCHAR g_szCurrentKeyName[4096]; DWORD g_dwCurrentKeyNameLength = 0; #else #define DBGTRACE(l, x) #endif VOID FreeSecurityDescriptors( VOID ) /*++ Routine Description: This function deallocates the data structures allocated and initialized by CreateDeviceParametersSD. 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; } } 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 FreeDeviceParametersSD. Arguments: None. Return Value: Pointer to the initialized Security Descriptor. NULL is returned if an error occurs. --*/ { SID_IDENTIFIER_AUTHORITY NtAuthority = SECURITY_NT_AUTHORITY; EXPLICIT_ACCESS ExplicitAccess[2]; DWORD dwError; BOOL bSuccess; DWORD i; // // 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); 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 < 2; 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_pSystemSid; ExplicitAccess[1].grfAccessPermissions = KEY_ALL_ACCESS; ExplicitAccess[1].Trustee.ptstrName = (LPTSTR)g_pAdminSid; // // Create the DACL with the both the above ACEs for the DeviceParameters // dwError = SetEntriesInAcl( 2, ExplicitAccess, NULL, &g_pDeviceParametersDacl ); if (dwError == ERROR_SUCCESS) { // // Create the DACL with just the system ACE for the locked private // keys. // dwError = SetEntriesInAcl( 1, ExplicitAccess, 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(); } return bSuccess; } 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 DBG || UMODETEST DWORD dwStartKeyNameLength = g_dwCurrentKeyNameLength; if (g_dwCurrentKeyNameLength != 0) { g_szCurrentKeyName[ g_dwCurrentKeyNameLength++ ] = TEXT('\\'); } _tcscpy(&g_szCurrentKeyName[g_dwCurrentKeyNameLength], pszKeyName); g_dwCurrentKeyNameLength += _tcslen(pszKeyName); #endif 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 DBG || UMODETEST g_dwCurrentKeyNameLength = dwStartKeyNameLength; g_szCurrentKeyName[g_dwCurrentKeyNameLength] = TEXT('\0'); #endif } VOID LockUnlockEnumTree( LPTSTR pszMachineName, BOOL bLock ) { PSECURITY_DESCRIPTOR pSD; HKEY hParentKey; LONG RegStatus; if (pszMachineName != NULL) { RegStatus = RegConnectRegistry( pszMachineName, HKEY_LOCAL_MACHINE, &hParentKey ); if (RegStatus != ERROR_SUCCESS) { DBGTRACE( DBGF_ERRORS, (TEXT("Could not connect to remote registry on %s, status = %d\n"), pszMachineName, RegStatus) ); } } else { hParentKey = HKEY_LOCAL_MACHINE; } if (CreateSecurityDescriptors()) { EnumKeysAndApplyDacls( hParentKey, REGSTR_PATH_SYSTEMENUM, 0, FALSE, !bLock, bLock ? &g_LockedPrivateKeysSD : &g_DeviceParametersSD, &g_DeviceParametersSD ); FreeSecurityDescriptors(); } } #if DBG || UMODETEST void RegFixDebugMessage(LPWSTR format, ...) { va_list args; va_start(args, format); _vtprintf(format, args); } #endif #if UMODETEST void usage(int argc, TCHAR **argv) { PTCHAR pszProgram; if ((pszProgram = _tcsrchr(argv[0], TEXT('\\'))) != NULL) { pszProgram++; } else { pszProgram = argv[0]; } _tprintf(TEXT("%s: Lock or Unlock PnP Registry (Enum key)\n\n"), pszProgram); _tprintf(TEXT("Usage: %s [-m ] -l | -u\n"), pszProgram); _tprintf(TEXT(" -m Remote machine without leading \\\\\n")); _tprintf(TEXT(" -l Locks Enum key\n")); _tprintf(TEXT(" -u Unlocks Enum key\n\n")); _tprintf(TEXT("Note: -m is optional. Only one of -l or -u may be used.\n")); } int __cdecl _tmain(int argc, TCHAR **argv) { LPTSTR pszMachineName = NULL; LPTSTR pszArg; int idxArg; if ( argc == 1 ) { usage(argc, argv); return 0; } for (idxArg = 1; idxArg < argc; idxArg++) { pszArg = argv[ idxArg ]; if (*pszArg == '/' || *pszArg == '-') { pszArg++; while (pszArg != NULL && *pszArg != '\0') { switch (*pszArg) { case '/': // Ignore these, caused by cmds like /m/l pszArg++; break; case 'l': case 'L': pszArg++; LockUnlockEnumTree( pszMachineName, TRUE ); break; case 'm': case 'M': pszArg++; if (*pszArg == ':' || *pszArg == '=') { if (pszArg[ 1 ] != '\0') { pszMachineName = ++pszArg; } } else if (*pszArg != '\0') { pszMachineName = pszArg; } else if ((idxArg + 1) < argc && (argv[ idxArg + 1 ][0] != '/' && argv[ idxArg + 1 ][0] != '-')) { pszMachineName = argv[ ++idxArg ]; } if (pszMachineName == NULL) { _tprintf( TEXT("%c%c : missing machine name argument\n"), argv[ idxArg ][ 0 ], pszArg [ - 1 ] ); usage(argc, argv); return 1; } pszArg = NULL; break; case 'u': case 'U': pszArg++; LockUnlockEnumTree( pszMachineName, FALSE ); break; case 'v': case 'V': pszArg++; g_RegFixDebugFlag |= DBGF_REGISTRY; break; default: _tprintf( TEXT("%c%c : invalid option\n"), argv[ idxArg ][ 0 ], *pszArg ); break; } } } } return 0; } #endif