//+------------------------------------------------------------------------- // // Microsoft Windows // Copyright (C) Microsoft Corporation, 1996. // // registry.cxx // // Registry related routines // //-------------------------------------------------------------------------- #include "act.hxx" HKEY ghClsidMachine = 0; HKEY ghAppidMachine = 0; #ifdef _CHICAGO_ // Global flags set to default values BOOL gbEnableRemoteLaunch = FALSE; BOOL gbEnableRemoteConnect = FALSE; #endif // _CHICAGO_ #ifdef SERVER_HANDLER BOOL gbDisableEmbeddingServerHandler = FALSE; #endif // SERVER_HANDLER BOOL gbSAFERROTChecksEnabled = TRUE; BOOL gbSAFERAAAChecksEnabled = TRUE; BOOL gbDynamicIPChangesEnabled = TRUE; // On in whistler; was off by default in W2K DWORD gdwTimeoutPeriodForStaleMids = 10 * 60 * 1000; // ten minutes //------------------------------------------------------------------------- // // ReadStringValue // // Returns the named value string under the specified open registry key. // // Returns : // // ERROR_SUCCESS, ERROR_FILE_NOT_FOUND, ERROR_BAD_FORMAT, ERROR_OUTOFMEMORY, // ERROR_BAD_PATHNAME, or other more esoteric win32 error code. // //------------------------------------------------------------------------- DWORD ReadStringValue( IN HKEY hKey, IN WCHAR * pwszValueName, OUT WCHAR ** ppwszString ) { DWORD Status; DWORD Type; DWORD StringSize; WCHAR * pwszScan; WCHAR * pwszSource; WCHAR wszString[64]; *ppwszString = 0; StringSize = sizeof(wszString); Status = RegQueryValueEx( hKey, pwszValueName, NULL, &Type, (BYTE *) wszString, &StringSize ); if ( (ERROR_SUCCESS == Status) && (Type != REG_SZ) && (Type != REG_MULTI_SZ) && (Type != REG_EXPAND_SZ) ) Status = ERROR_BAD_FORMAT; if ( (Status != ERROR_SUCCESS) && (Status != ERROR_MORE_DATA) ) return Status; // Allocate one extra WCHAR for an extra null at the end. *ppwszString = (WCHAR *) PrivMemAlloc( StringSize + sizeof(WCHAR) ); if ( ! *ppwszString ) return ERROR_OUTOFMEMORY; if ( ERROR_MORE_DATA == Status ) { Status = RegQueryValueEx( hKey, pwszValueName, NULL, &Type, (BYTE *) *ppwszString, &StringSize ); if ( Status != ERROR_SUCCESS ) { PrivMemFree( *ppwszString ); *ppwszString = 0; return Status; } } else { memcpy( *ppwszString, wszString, StringSize ); } // // Put an extra null at the end. This allows using identical logic for both // REG_SZ and REG_MULTI_SZ values like RemoteServerNames instead of special // casing it. // (*ppwszString)[StringSize/sizeof(WCHAR)] = 0; // // Don't bother with any of the following conversions for multi strings. // They better be in the correct format. // if ( REG_MULTI_SZ == Type ) return Status; pwszScan = pwszSource = *ppwszString; // // The original OLE sources had logic for stripping out a quoted // value. I have no idea on the origin of this or if it is still // important. It shouldn't hurt anything to keep it in to save // us from some nasty compatability problem. // - DKays, 8/96 // if ( L'\"' == *pwszScan ) { pwszScan++; // Copy everything between the quotes. while ( *pwszScan && (*pwszScan != L'\"') ) *pwszSource++ = *pwszScan++; *pwszSource = 0; } // // Leading and trailing whitespace would hose us for some values, like // RemoteServerName or RunAs. These are stripped here. Once again, only // good things can happen if we put this logic in. // pwszScan = *ppwszString; while ( *pwszScan && ((L' ' == *pwszScan) || (L'\t' == *pwszScan)) ) pwszScan++; if ( ! *pwszScan ) { PrivMemFree( *ppwszString ); *ppwszString = 0; return ERROR_BAD_PATHNAME; } if ( *ppwszString < pwszScan ) lstrcpyW( *ppwszString, pwszScan ); pwszScan = *ppwszString + lstrlenW(*ppwszString); while ( (pwszScan != *ppwszString) && ((L' ' == pwszScan[-1]) || (L'\t' == pwszScan[-1])) ) pwszScan--; *pwszScan = 0; // // Finally, handle environment string expansion if necessary. // Remember to add the extra trailing null again. // #ifndef _CHICAGO_ if ( REG_EXPAND_SZ == Type ) { WCHAR * pwszExpandedString; DWORD ExpandedStringSize; pwszExpandedString = 0; StringSize /= sizeof(WCHAR); for (;;) { PrivMemFree( pwszExpandedString ); pwszExpandedString = (WCHAR *) PrivMemAlloc( (StringSize + 1) * sizeof(WCHAR) ); if ( ! pwszExpandedString ) { Status = ERROR_OUTOFMEMORY; break; } ExpandedStringSize = ExpandEnvironmentStrings( *ppwszString, pwszExpandedString, StringSize ); if ( ! ExpandedStringSize ) { Status = GetLastError(); break; } if ( ExpandedStringSize > StringSize ) { StringSize = ExpandedStringSize; continue; } Status = ERROR_SUCCESS; break; } PrivMemFree( *ppwszString ); if ( ERROR_SUCCESS == Status ) { pwszExpandedString[lstrlenW(pwszExpandedString)+1] = 0; *ppwszString = pwszExpandedString; } else { PrivMemFree( pwszExpandedString ); *ppwszString = 0; } } #endif return Status; } //------------------------------------------------------------------------- // // ReadStringKeyValue // // Reads the unnamed named value string for the specified subkey name // under the given open registry key. // //------------------------------------------------------------------------- DWORD ReadStringKeyValue( IN HKEY hKey, IN WCHAR * pwszKeyName, OUT WCHAR ** ppwszString ) { DWORD Status; HKEY hSubKey; Status = RegOpenKeyEx( hKey, pwszKeyName, NULL, KEY_READ, &hSubKey ); if ( Status != ERROR_SUCCESS ) return Status; Status = ReadStringValue( hSubKey, L"", ppwszString ); RegCloseKey( hSubKey ); return Status; } #ifndef _CHICAGO_ //------------------------------------------------------------------------- // // ReadSecurityDescriptor // // Converts a security descriptor from self relative to absolute form. // Stuffs in an owner and a group. // // Notes : // // REGDB_E_INVALIDVALUE is returned when there is something // at the specified value, but it is not a security descriptor. // //------------------------------------------------------------------------- DWORD ReadSecurityDescriptor( IN HKEY hKey, IN WCHAR * pwszValue, OUT CSecDescriptor ** ppCSecDescriptor ) { PSID pGroupSid; PSID pOwnerSid; DWORD Size; DWORD Type; DWORD Status; DWORD Size2; SECURITY_DESCRIPTOR* pSD = NULL; CSecDescriptor* pCSecDescriptor = NULL; // Find put how much memory to allocate for the security descriptor. Size = 0; *ppCSecDescriptor = NULL; Status = RegQueryValueEx( hKey, pwszValue, 0, &Type, 0, &Size ); Size2 = Size; if ( Status != ERROR_SUCCESS ) return Status; if ( Type != REG_BINARY || (Size < sizeof(SECURITY_DESCRIPTOR)) ) return ERROR_BAD_FORMAT; // // Allocate memory for the security descriptor plus the owner and // group SIDs. // #ifdef _WIN64 { DWORD deltaSize = sizeof( SECURITY_DESCRIPTOR ) - sizeof( SECURITY_DESCRIPTOR_RELATIVE ); ASSERT( deltaSize < sizeof( SECURITY_DESCRIPTOR ) ); deltaSize = OLE2INT_ROUND_UP( deltaSize, sizeof(PVOID) ); Size2 += deltaSize; } #endif // _WIN64 // Allocate sd buffer and wrapper class pSD = (SECURITY_DESCRIPTOR *) PrivMemAlloc( Size2 ); if (!pSD) return ERROR_OUTOFMEMORY; // Read the security descriptor. Status = RegQueryValueEx( hKey, pwszValue, 0, &Type, (PBYTE) pSD, &Size ); if ( Status != ERROR_SUCCESS ) goto ReadSecurityDescriptorEnd; if ( Type != REG_BINARY ) { Status = ERROR_BAD_FORMAT; goto ReadSecurityDescriptorEnd; } // // Fix up the security descriptor. // #ifdef _WIN64 if ( MakeAbsoluteSD2( pSD, &Size2 ) == FALSE ) { Status = ERROR_BAD_FORMAT; goto ReadSecurityDescriptorEnd; } #else // !_WIN64 pSD->Control &= ~SE_SELF_RELATIVE; pSD->Sacl = NULL; if ( pSD->Dacl != NULL ) { if ( (Size < sizeof(ACL) + sizeof(SECURITY_DESCRIPTOR)) || ((ULONG) pSD->Dacl > Size - sizeof(ACL)) ) { Status = ERROR_BAD_FORMAT; goto ReadSecurityDescriptorEnd; } pSD->Dacl = (ACL *) (((char *) pSD) + ((ULONG) pSD->Dacl)); if ( pSD->Dacl->AclSize + sizeof(SECURITY_DESCRIPTOR) > Size ) { Status = ERROR_BAD_FORMAT; goto ReadSecurityDescriptorEnd; } } // Set up the owner and group SIDs. if ( pSD->Group == 0 || ((ULONG)pSD->Group) + sizeof(SID) > Size || pSD->Owner == 0 || ((ULONG)pSD->Owner) + sizeof(SID) > Size ) { Status = ERROR_BAD_FORMAT; goto ReadSecurityDescriptorEnd; } pSD->Group = (SID *) (((BYTE *) pSD) + (ULONG) (pSD)->Group); pSD->Owner = (SID *) (((BYTE *) pSD) + (ULONG) (pSD)->Owner); #endif // !_WIN64 ReadSecurityDescriptorEnd: if ( Status != ERROR_SUCCESS ) { if ( pSD ) PrivMemFree( pSD ); return Status; } // Allocate wrapper class for refcount semantics pCSecDescriptor = new CSecDescriptor(pSD); if (!pCSecDescriptor) { PrivMemFree(pSD); return ERROR_OUTOFMEMORY; } ASSERT( IsValidSecurityDescriptor( pCSecDescriptor->GetSD()) ); // New class has refcount of 1, owned by the caller *ppCSecDescriptor = pCSecDescriptor; return ERROR_SUCCESS; } #endif LONG OpenClassesRootKeys() { LONG RegStatus; DWORD Disposition; if ( ! ghClsidMachine ) { // This may fail during GUI mode setup. RegStatus = RegOpenKeyEx( HKEY_CLASSES_ROOT, L"ClsID", 0, KEY_READ, &ghClsidMachine ); if ( RegStatus != ERROR_SUCCESS ) return RegStatus; } if ( ! ghAppidMachine ) { // This may fail during GUI mode setup. RegStatus = RegCreateKeyEx( HKEY_CLASSES_ROOT, TEXT("AppID"), 0, NULL, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &ghAppidMachine, &Disposition ); if ( RegStatus != ERROR_SUCCESS ) return RegStatus; } return ERROR_SUCCESS; } //------------------------------------------------------------------------- // // InitSCMRegistry // // Opens global registry keys and settings. // //------------------------------------------------------------------------- HRESULT InitSCMRegistry() { HRESULT hr; LONG err; DWORD dwDisp; ReadRemoteActivationKeys(); ReadRemoteBindingHandleCacheKeys(); ReadSAFERKeys(); ReadDynamicIPChangesKeys(); (void) OpenClassesRootKeys(); // Now read the actual values from the registry // Check if Embedding Server Handler is enabled #ifdef SERVER_HANDLER HKEY hkeyOle; err = RegOpenKeyExA( HKEY_LOCAL_MACHINE, "SOFTWARE\\Microsoft\\OLE\\DisableEmbeddingServerHandler", NULL, KEY_QUERY_VALUE, &hkeyOle ); if (err == ERROR_SUCCESS) { gbDisableEmbeddingServerHandler=TRUE; RegCloseKey(hkeyOle); } #endif // SERVER_HANDLER return S_OK; } //------------------------------------------------------------------------- // // ReadRemoteActivationKeys // //------------------------------------------------------------------------- void ReadRemoteActivationKeys() { DWORD err; HKEY hOle; // Read the DefaultLaunchPermission value (if present) from the registry if ((err = RegOpenKeyEx(HKEY_LOCAL_MACHINE, L"SOFTWARE\\Microsoft\\OLE", NULL, KEY_READ, &hOle)) == ERROR_SUCCESS) { DWORD dwStatus; CSecDescriptor* pSecDescriptor = NULL; dwStatus = ReadSecurityDescriptor( hOle, L"DefaultLaunchPermission", &pSecDescriptor); if (dwStatus == ERROR_SUCCESS) { ASSERT(pSecDescriptor); SetDefaultLaunchPermissions(pSecDescriptor); pSecDescriptor->DecRefCount(); } else { // In case of a non-existent or malformed descriptor, reset // current perms to NULL - this blocks everybody. ASSERT(!pSecDescriptor); SetDefaultLaunchPermissions(NULL); } RegCloseKey(hOle); } } //------------------------------------------------------------------------- // // GetActivationFailureLoggingLevel // // Returns current activation failure logging level as specified // by a certain key in the Registry. // // History: // // a-sergiv 6-17-99 Created // // Returns : // // 0 = Discretionary logging. Log by default, client can override // 1 = Always log. Log all errors no matter what client specified // 2 = Never log. Never log error no matter what client speciied // //------------------------------------------------------------------------- DWORD GetActivationFailureLoggingLevel() { DWORD err; DWORD dwSize; DWORD dwType; HKEY hOle; DWORD dwLevel = 0; if ((err = RegOpenKeyEx(HKEY_LOCAL_MACHINE, L"SOFTWARE\\Microsoft\\OLE", NULL, KEY_READ, &hOle)) == ERROR_SUCCESS) { dwSize = sizeof(DWORD); if ((err = RegQueryValueEx(hOle, L"ActivationFailureLoggingLevel", NULL, &dwType, (BYTE *) &dwLevel, &dwSize)) == ERROR_SUCCESS) { // Valid values are 0, 1 and 2. The variable is unsigned. // Assume 0 if invalid value is specified. if(dwLevel > 2) dwLevel = 0; } else { // Assume 0 if not specified dwLevel = 0; } RegCloseKey(hOle); } return dwLevel; } // // ReadRegistryIntegerValue // // Tries to read a numeric value from the specified value under the key. // Returns FALSE if it doesn't exist or is of the wrong type, TRUE // otherwise. // BOOL ReadRegistryIntegerValue(HKEY hkey, WCHAR* pwszValue, DWORD* pdwValue) { BOOL bResult = FALSE; DWORD error; DWORD dwSize = sizeof(DWORD); DWORD dwType; DWORD dwValue; error = RegQueryValueExW(hkey, pwszValue, NULL, &dwType, (BYTE*)&dwValue, &dwSize); if (error == ERROR_SUCCESS && dwType == REG_DWORD) { *pdwValue = dwValue; bResult = TRUE; } return bResult; } //------------------------------------------------------------------------- // // ReadRemoteBindingHandleCacheKeys // // Reads optional values from the registry to control the behavior of the // remote binding handle cache (gpRemoteMachineList). // //------------------------------------------------------------------------- void ReadRemoteBindingHandleCacheKeys() { HKEY hOle; DWORD error; error = RegOpenKeyEx(HKEY_LOCAL_MACHINE, L"SOFTWARE\\Microsoft\\OLE", NULL, KEY_READ, &hOle); if (error == ERROR_SUCCESS) { DWORD dwValue; if (ReadRegistryIntegerValue(hOle, L"RemoteHandleCacheMaxSize", &dwValue)) { gdwRemoteBindingHandleCacheMaxSize = dwValue; } if (ReadRegistryIntegerValue(hOle, L"RemoteHandleCacheMaxLifetime", &dwValue)) { gdwRemoteBindingHandleCacheMaxLifetime = dwValue; } if (ReadRegistryIntegerValue(hOle, L"RemoteHandleCacheMaxIdleTimeout", &dwValue)) { gdwRemoteBindingHandleCacheIdleTimeout = dwValue; } // This one not really a "binding handle cache" knob. // CODEWORK: all of this registry knob reading code needs to be // cleaned-up and rewritten. if (ReadRegistryIntegerValue(hOle, L"StaleMidTimeout", &dwValue)) { gdwTimeoutPeriodForStaleMids = dwValue; } RegCloseKey(hOle); } return; } //------------------------------------------------------------------------- // // ReadSAFERKeys // // Reads optional values from the registry to control aspects of our // SAFER windows support // //------------------------------------------------------------------------- void ReadSAFERKeys() { HKEY hOle; DWORD error; DWORD dwType; DWORD dwSize; WCHAR wszYN[5]; error = RegOpenKeyEx(HKEY_LOCAL_MACHINE, L"SOFTWARE\\Microsoft\\OLE", NULL, KEY_READ, &hOle); if (error == ERROR_SUCCESS) { dwSize = sizeof(wszYN) / sizeof(WCHAR); error = RegQueryValueEx(hOle, L"SRPRunningObjectChecks", NULL, &dwType, (BYTE*)wszYN, &dwSize); if (error == ERROR_SUCCESS && (wszYN[0] == L'n' || wszYN[0] == L'N')) { gbSAFERROTChecksEnabled = FALSE; } dwSize = sizeof(wszYN) / sizeof(WCHAR); error = RegQueryValueEx(hOle, L"SRPActivateAsActivatorChecks", NULL, &dwType, (BYTE*)wszYN, &dwSize); if (error == ERROR_SUCCESS && (wszYN[0] == L'n' || wszYN[0] == L'N')) { gbSAFERAAAChecksEnabled = FALSE; } CloseHandle(hOle); } return; } //------------------------------------------------------------------------- // // ReadDynamicIPChangesKeys // // Reads optional values from the registry to control aspects of our // dynamic IP change support. // //------------------------------------------------------------------------- void ReadDynamicIPChangesKeys() { HKEY hOle; DWORD error; DWORD dwType; DWORD dwSize; WCHAR wszYN[5]; error = RegOpenKeyEx(HKEY_LOCAL_MACHINE, L"SOFTWARE\\Microsoft\\OLE", NULL, KEY_READ, &hOle); if (error == ERROR_SUCCESS) { dwSize = sizeof(wszYN) / sizeof(WCHAR); error = RegQueryValueEx(hOle, L"EnableSystemDynamicIPTracking", NULL, &dwType, (BYTE*)wszYN, &dwSize); // note: in w2k, this code turned the flag on only if the registry // value was "y" or "Y". In whistler I have reversed these semantics. if (error == ERROR_SUCCESS && (wszYN[0] == L'n' || wszYN[0] == L'N')) { gbDynamicIPChangesEnabled = FALSE; } CloseHandle(hOle); } return; } //------------------------------------------------------------------------- // // CRegistryWatcher::CRegistryWatcher // // Constructor for CRegistryWatcher. // // If allocation of the event fails, or opening the key fails, or registering // for the notify fails, then Changed() always returns S_OK (yes, it changed). // This doesn't affect correctness, only speed. // //------------------------------------------------------------------------- CRegistryWatcher::CRegistryWatcher(HKEY hKeyRoot, const WCHAR *wszSubKey) { _fValid = FALSE; _hEvent = CreateEvent(NULL, FALSE, TRUE, NULL); if (!_hEvent) return; LONG res = RegOpenKeyEx(hKeyRoot, wszSubKey, 0, KEY_NOTIFY, &_hWatchedKey); if (res != ERROR_SUCCESS) { Cleanup(); return; } _fValid = TRUE; } //------------------------------------------------------------------------- // // CRegistryWatcher::CRegistryWatcher // // Determine if the registry key being watched has changed. Returns // S_OK if it has changed, S_FALSE if not, and an error if something // failed. // //------------------------------------------------------------------------- HRESULT CRegistryWatcher::Changed() { if (!_fValid) return S_OK; // _hEvent is auto-reset, so only one thread will re-register. if (WAIT_OBJECT_0 == WaitForSingleObject(_hEvent, 0)) { LONG res = RegNotifyChangeKeyValue(_hWatchedKey, TRUE, REG_NOTIFY_CHANGE_NAME | REG_NOTIFY_CHANGE_LAST_SET, _hEvent, TRUE); if (res != ERROR_SUCCESS) { // Could not re-register, so we can't watch the key anymore. _fValid = FALSE; return HRESULT_FROM_WIN32(res); } else return S_OK; } else return S_FALSE; } void CRegistryWatcher::Cleanup() { if (_hEvent) { CloseHandle(_hEvent); _hEvent = NULL; } if (_hWatchedKey) { RegCloseKey(_hWatchedKey); _hWatchedKey = NULL; } }