/*++ Copyright (c) 1995 Microsoft Corporation Module Name: regsec.cpp Abstract: ISecurityInformation implementation for Registry Key Author: Hitesh Raigandhi (raigah) May-1999 Revision History: --*/ //Include Files: // #include "wchar.h" #include "regsec.h" #include "regresid.h" #include "assert.h" #define STRSAFE_NO_DEPRECATE #include // ISecurityInformation interface implementation EXTERN_C const GUID IID_ISecurityInformation ; // = { 0x965fc360, 0x16ff, 0x11d0, 0x91, 0xcb, 0x0, 0xaa, 0x0, 0xbb, 0xb7, 0x23 }; //The Following array defines the permission names for Registry Key Objects SI_ACCESS siKeyAccesses[] = { { NULL, KEY_ALL_ACCESS, MAKEINTRESOURCE(IDS_SEC_EDITOR_FULL_ACCESS), SI_ACCESS_GENERAL | CONTAINER_INHERIT_ACE | SI_ACCESS_SPECIFIC }, { NULL, KEY_QUERY_VALUE, MAKEINTRESOURCE(IDS_SEC_EDITOR_QUERY_VALUE), SI_ACCESS_SPECIFIC | CONTAINER_INHERIT_ACE }, { NULL, KEY_SET_VALUE, MAKEINTRESOURCE(IDS_SEC_EDITOR_SET_VALUE), SI_ACCESS_SPECIFIC | CONTAINER_INHERIT_ACE }, { NULL, KEY_CREATE_SUB_KEY, MAKEINTRESOURCE(IDS_SEC_EDITOR_CREATE_SUBKEY), SI_ACCESS_SPECIFIC | CONTAINER_INHERIT_ACE }, { NULL, KEY_ENUMERATE_SUB_KEYS, MAKEINTRESOURCE(IDS_SEC_EDITOR_ENUM_SUBKEYS), SI_ACCESS_SPECIFIC | CONTAINER_INHERIT_ACE }, { NULL, KEY_NOTIFY, MAKEINTRESOURCE(IDS_SEC_EDITOR_NOTIFY), SI_ACCESS_SPECIFIC | CONTAINER_INHERIT_ACE }, { NULL, KEY_CREATE_LINK, MAKEINTRESOURCE(IDS_SEC_EDITOR_CREATE_LINK), SI_ACCESS_SPECIFIC | CONTAINER_INHERIT_ACE }, { NULL, 0x00010000, /* DELETE, */ MAKEINTRESOURCE(IDS_SEC_EDITOR_DELETE), SI_ACCESS_SPECIFIC | CONTAINER_INHERIT_ACE }, { NULL, WRITE_DAC, MAKEINTRESOURCE(IDS_SEC_EDITOR_WRITE_DAC), SI_ACCESS_SPECIFIC | CONTAINER_INHERIT_ACE }, { NULL, WRITE_OWNER, MAKEINTRESOURCE(IDS_SEC_EDITOR_WRITE_OWNER), SI_ACCESS_SPECIFIC | CONTAINER_INHERIT_ACE }, { NULL, READ_CONTROL, MAKEINTRESOURCE(IDS_SEC_EDITOR_READ_CONTROL), SI_ACCESS_SPECIFIC | CONTAINER_INHERIT_ACE }, { NULL, KEY_READ, MAKEINTRESOURCE(IDS_SEC_EDITOR_READ), SI_ACCESS_GENERAL | CONTAINER_INHERIT_ACE }, }; // The following array defines the inheritance types for Registry. // // // For Keys, objects and containers are the same, so no need for OBJECT_INHERIT_ACE // SI_INHERIT_TYPE siKeyInheritTypes[] = { NULL, 0, MAKEINTRESOURCE(IDS_KEY_FOLDER), NULL, CONTAINER_INHERIT_ACE, MAKEINTRESOURCE(IDS_KEY_FOLDER_SUBFOLDER), NULL, INHERIT_ONLY_ACE | CONTAINER_INHERIT_ACE, MAKEINTRESOURCE(IDS_KEY_SUBFOLDER_ONLY) }; #define iKeyDefAccess 10 // index of value in array siKeyAccesses #ifndef ARRAYSIZE #define ARRAYSIZE(x) (sizeof(x)/sizeof(x[0])) #endif PWSTR _PredefinedKeyName[] = { L"CLASSES_ROOT", L"CURRENT_USER", L"MACHINE", L"USERS", L"CONFIG" }; //CKeySecurityInformation Functions HRESULT CKeySecurityInformation::Initialize( LPCWSTR strKeyName, LPCWSTR strParentName, LPCWSTR strMachineName, LPCWSTR strPageTitle, BOOL bRemote, PREDEFINE_KEY PredefinedKey, BOOL bReadOnly, HWND hWnd) { if( strParentName ) if( !strKeyName ) return HRESULT_FROM_WIN32(ERROR_INVALID_PARAMETER); if( NULL == strMachineName ) return HRESULT_FROM_WIN32(ERROR_INVALID_PARAMETER); if( NULL == hWnd ) return HRESULT_FROM_WIN32(ERROR_INVALID_PARAMETER); if( NULL == strPageTitle ) return HRESULT_FROM_WIN32(ERROR_INVALID_PARAMETER); AuthzInitializeResourceManager(AUTHZ_RM_FLAG_NO_AUDIT, NULL, NULL, NULL, 0, &m_ResourceManager ); HRESULT hr = S_OK; m_strKeyName = strKeyName; m_strParentName = strParentName; m_strMachineName = strMachineName; m_strPageTitle = strPageTitle; m_bRemote = bRemote; m_PredefinedKey = PredefinedKey; m_bReadOnly = bReadOnly; m_hWnd = hWnd; m_dwFlags = SI_EDIT_ALL | SI_ADVANCED | SI_CONTAINER | SI_RESET_DACL_TREE | SI_RESET_SACL_TREE | SI_OWNER_RECURSE | SI_PAGE_TITLE | SI_EDIT_EFFECTIVE; if( m_bReadOnly ) m_dwFlags |= SI_READONLY | SI_OWNER_READONLY ; //Set Handle to Predfined key if( S_OK != ( hr = SetHandleToPredefinedKey() ) ) return hr; //Set CompleteName if( S_OK != ( hr = SetCompleteName() ) ) return hr; return S_OK; } CKeySecurityInformation::~CKeySecurityInformation() { if( m_strCompleteName ) LocalFree( m_strCompleteName ); //Close Handle to Remote Registry if it was successfully opened. if( m_bRemote && m_hkeyPredefinedKey ) RegCloseKey(m_hkeyPredefinedKey); AuthzFreeResourceManager(m_ResourceManager); } //Sets the complete name in Format: // "\\machine_name\Predefined_keyName\regkey_path HRESULT CKeySecurityInformation::SetCompleteName() { UINT len = 0; PWSTR pstrCompleteName; if( m_bRemote ) { len += wcslen( m_strMachineName ); len++; } len += wcslen(_PredefinedKeyName[m_PredefinedKey]); len++; if( m_strParentName ) { len += wcslen(m_strParentName); len++; } if( m_strKeyName ) { len += wcslen(m_strKeyName); len++; } len++; //Terminating null pstrCompleteName = (PWSTR)LocalAlloc(LPTR, len * sizeof(WCHAR)); if( pstrCompleteName == NULL ) return HRESULT_FROM_WIN32(ERROR_NOT_ENOUGH_MEMORY); if( m_bRemote ) { StringCchCopyW(pstrCompleteName, len, m_strMachineName); StringCchCatW(pstrCompleteName, len, L"\\"); } StringCchCatW(pstrCompleteName, len, _PredefinedKeyName[m_PredefinedKey]); StringCchCatW(pstrCompleteName, len, L"\\"); if(m_strParentName) { StringCchCatW(pstrCompleteName, len, m_strParentName); StringCchCatW(pstrCompleteName, len, L"\\"); } if(m_strKeyName) { StringCchCatW(pstrCompleteName, len, m_strKeyName ); StringCchCatW(pstrCompleteName, len, L"\\"); } m_strCompleteName = pstrCompleteName; return S_OK; } //Doesn't have Predefined key name attached //Caller must LocalFree LPCWSTR CKeySecurityInformation::GetCompleteName1() { UINT len = 0; PWSTR pstrCompleteName; if( m_strParentName ) { len += wcslen(m_strParentName); len++; } if( m_strKeyName ) { len += wcslen(m_strKeyName); len++; } len++; //Terminating null pstrCompleteName = (PWSTR)LocalAlloc(LPTR, len * sizeof(WCHAR)); if( pstrCompleteName == NULL ) return NULL; if(m_strParentName) { StringCchCopyW(pstrCompleteName, len, m_strParentName); StringCchCatW(pstrCompleteName, len, L"\\"); } if(m_strKeyName) { StringCchCatW(pstrCompleteName, len, m_strKeyName ); StringCchCatW(pstrCompleteName, len, L"\\"); } return pstrCompleteName; } HRESULT CKeySecurityInformation::SetHandleToPredefinedKey() { DWORD dwErr; HRESULT hr = S_OK; if( !m_bRemote ) { switch ( m_PredefinedKey ){ case PREDEFINE_KEY_CLASSES_ROOT: m_hkeyPredefinedKey= HKEY_CLASSES_ROOT; break; case PREDEFINE_KEY_CURRENT_USER: m_hkeyPredefinedKey = HKEY_CURRENT_USER; break; case PREDEFINE_KEY_LOCAL_MACHINE : m_hkeyPredefinedKey = HKEY_LOCAL_MACHINE; break; case PREDEFINE_KEY_USERS: m_hkeyPredefinedKey = HKEY_USERS; break; case PREDEFINE_KEY_CURRENT_CONFIG : m_hkeyPredefinedKey = HKEY_CURRENT_CONFIG; break; default: //assert(false); break; } } else { //IsRemoteRegistry switch ( m_PredefinedKey ){ case PREDEFINE_KEY_CLASSES_ROOT: case PREDEFINE_KEY_CURRENT_USER: case PREDEFINE_KEY_CURRENT_CONFIG: m_hkeyPredefinedKey = 0; break; case PREDEFINE_KEY_LOCAL_MACHINE : m_hkeyPredefinedKey = HKEY_LOCAL_MACHINE; break; case PREDEFINE_KEY_USERS: m_hkeyPredefinedKey = HKEY_USERS; break; default: //assert(false); break; } if( m_hkeyPredefinedKey ){ dwErr = RegConnectRegistry( m_strMachineName, m_hkeyPredefinedKey, &m_hkeyPredefinedKey ); if( dwErr ) { m_hkeyPredefinedKey = 0; hr = HRESULT_FROM_WIN32( dwErr ); } } } //IsRemoteRegistry return hr; } /* JeffreyS 1/24/97: If you don't set the SI_RESET flag in ISecurityInformation::GetObjectInformation, then fDefault should never be TRUE so you can ignore it. Returning E_NOTIMPL in this case is OK too. If you want the user to be able to reset the ACL to some default state (defined by you) then turn on SI_RESET and return your default ACL when fDefault is TRUE. This happens if/when the user pushes a button that is only visible when SI_RESET is on. */ STDMETHODIMP CKeySecurityInformation::GetObjectInformation ( PSI_OBJECT_INFO pObjectInfo ) { assert( NULL != pObjectInfo ); pObjectInfo->dwFlags = m_dwFlags; pObjectInfo->hInstance = GetModuleHandle(NULL); // pObjectInfo->pszServerName = (LPWSTR)m_strMachineName; pObjectInfo->pszServerName = (LPWSTR)m_strMachineName; pObjectInfo->pszObjectName = (LPWSTR)m_strPageTitle; return S_OK; } STDMETHODIMP CKeySecurityInformation::GetAccessRights( const GUID *pguidObjectType, DWORD dwFlags, PSI_ACCESS *ppAccess, ULONG *pcAccesses, ULONG *piDefaultAccess ) { assert( NULL != ppAccess ); assert( NULL != pcAccesses ); assert( NULL != piDefaultAccess ); *ppAccess = siKeyAccesses; *pcAccesses = ARRAYSIZE(siKeyAccesses); *piDefaultAccess = iKeyDefAccess; return S_OK; } GENERIC_MAPPING KeyMap = { KEY_READ, KEY_WRITE, KEY_READ, KEY_ALL_ACCESS }; STDMETHODIMP CKeySecurityInformation::MapGeneric( const GUID *pguidObjectType, UCHAR *pAceFlags, ACCESS_MASK *pMask ) { //jeffreys // After returning from the object picker dialog, aclui passes //CONTAINER_INHERIT_ACE | OBJECT_INHERIT_ACE to MapGeneric for validation when // initializing Permission entry for dialog. //hiteshr: since registry keys don't have OBJECT_INHERIT_ACE, remove this FLAG, //this will cause "this keys and subkeys" to appear as default in combobox. *pAceFlags &= ~OBJECT_INHERIT_ACE; MapGenericMask(pMask, &KeyMap); return S_OK; } STDMETHODIMP CKeySecurityInformation::GetInheritTypes ( PSI_INHERIT_TYPE *ppInheritTypes, ULONG *pcInheritTypes ) { assert( NULL != ppInheritTypes ); assert( NULL != pcInheritTypes ); *ppInheritTypes = siKeyInheritTypes; *pcInheritTypes = ARRAYSIZE(siKeyInheritTypes); return S_OK; } STDMETHODIMP CKeySecurityInformation::PropertySheetPageCallback( HWND hwnd, UINT uMsg, SI_PAGE_TYPE uPage ) { switch (uMsg) { case PSPCB_SI_INITDIALOG: m_hWndProperty = hwnd; break; case PSPCB_RELEASE: m_hWndProperty = NULL; break; } return S_OK; } STDMETHODIMP CKeySecurityInformation::GetSecurity( IN SECURITY_INFORMATION RequestedInformation, OUT PSECURITY_DESCRIPTOR *ppSecurityDescriptor, IN BOOL fDefault ) { if( fDefault ) return E_NOTIMPL; assert( NULL != ppSecurityDescriptor ); HRESULT hr; LPCTSTR pstrKeyName = GetCompleteName(); DWORD dwErr = 0; dwErr = GetNamedSecurityInfo( (LPTSTR)pstrKeyName, SE_REGISTRY_KEY, RequestedInformation, NULL, NULL, NULL, NULL, ppSecurityDescriptor); return ( ( dwErr != ERROR_SUCCESS ) ? HRESULT_FROM_WIN32(dwErr) : S_OK); } STDMETHODIMP CKeySecurityInformation::SetSecurity(IN SECURITY_INFORMATION si, IN PSECURITY_DESCRIPTOR pSD ) { if( NULL == pSD ) return HRESULT_FROM_WIN32(ERROR_INVALID_PARAMETER); HRESULT hr = S_OK; SECURITY_INFORMATION siLocal = 0; SECURITY_DESCRIPTOR sdLocal = {0}; ACL daclEmpty = {0}; HKEY hkeyOld = NULL; HKEY hKeyNew = NULL; BOOL bWriteInfo = false; DWORD Error = 0; // // Create a security descriptor with no SACL and an // empty DACL for recursively resetting security // InitializeSecurityDescriptor(&sdLocal, SECURITY_DESCRIPTOR_REVISION); InitializeAcl(&daclEmpty, sizeof(daclEmpty), ACL_REVISION); SetSecurityDescriptorDacl(&sdLocal, TRUE, &daclEmpty, FALSE); SetSecurityDescriptorSacl(&sdLocal, TRUE, &daclEmpty, FALSE); // // If we need to recursively set the Owner, get the Owner & // Group from pSD. // if (si & SI_OWNER_RECURSE) { PSID psid; BOOL bDefaulted; assert(si & (OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION)); siLocal |= si & (OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION); if (GetSecurityDescriptorOwner(pSD, &psid, &bDefaulted)) SetSecurityDescriptorOwner(&sdLocal, psid, bDefaulted); if (GetSecurityDescriptorGroup(pSD, &psid, &bDefaulted)) SetSecurityDescriptorGroup(&sdLocal, psid, bDefaulted); } if (si & SI_RESET_DACL_TREE) { assert(si & DACL_SECURITY_INFORMATION); siLocal |= si & DACL_SECURITY_INFORMATION; } if (si & SI_RESET_SACL_TREE) { assert(si & SACL_SECURITY_INFORMATION); siLocal |= si & SACL_SECURITY_INFORMATION; } if( siLocal ) { //Open the key with current Maximum Allowed Permisson //When applying permissons recursively , we first use current permisson, //if current permisson doesn't have enough rights, we reopen handle to key with //new permissons. If none (old or new )has enough permissons to enumerate child and // Query info we fail. REGSAM samDesired = MAXIMUM_ALLOWED; if( si & SACL_SECURITY_INFORMATION ) samDesired |= ACCESS_SYSTEM_SECURITY; if( si & DACL_SECURITY_INFORMATION ) samDesired |= WRITE_DAC; if( si & OWNER_SECURITY_INFORMATION ) samDesired |= WRITE_OWNER; //Open the selected key if( S_OK != ( hr = OpenKey( samDesired, &hkeyOld ) ) ){ return hr; } //Check if key has Enumeration Permisson DWORD NumberOfSubKeys = 0; DWORD MaxSubKeyLength = 0; // Find out the total number of subkeys Error = RegQueryInfoKey( hkeyOld, NULL, NULL, NULL, &NumberOfSubKeys, &MaxSubKeyLength, NULL, NULL, NULL, NULL, NULL, NULL ); if( Error != ERROR_SUCCESS ){ if( Error == ERROR_ACCESS_DENIED ) { hr = WriteObjectSecurity( hkeyOld, si, pSD ); if( hr != S_OK ) { if( m_hkeyPredefinedKey != hkeyOld ) RegCloseKey( hkeyOld ); return hr; } bWriteInfo = true; // // Handle doesn't allow KEY_QUERY_VALUE or READ_CONTROL access. // Open a new handle with these accesses. // samDesired = MAXIMUM_ALLOWED; if( si & SACL_SECURITY_INFORMATION ) { samDesired |= ACCESS_SYSTEM_SECURITY; } else if( si & DACL_SECURITY_INFORMATION ) { samDesired |= WRITE_DAC; } else if( si & OWNER_SECURITY_INFORMATION ) { samDesired |= WRITE_OWNER; } Error = RegOpenKeyEx( hkeyOld, NULL, REG_OPTION_RESERVED, samDesired, &hKeyNew ); if( Error != ERROR_SUCCESS ) { if( m_hkeyPredefinedKey != hkeyOld ) RegCloseKey( hkeyOld ); return HRESULT_FROM_WIN32(Error); } else { if( m_hkeyPredefinedKey != hkeyOld ) RegCloseKey( hkeyOld ); } Error = RegQueryInfoKey( hKeyNew, NULL, NULL, NULL, &NumberOfSubKeys, &MaxSubKeyLength, NULL, NULL, NULL, NULL, NULL, NULL ); if( Error != ERROR_SUCCESS ) { RegCloseKey( hKeyNew ); return HRESULT_FROM_WIN32( Error ); } } else { if( m_hkeyPredefinedKey != hkeyOld ) RegCloseKey( hkeyOld ); return HRESULT_FROM_WIN32( Error ); } } else hKeyNew = hkeyOld; if( NumberOfSubKeys ) { assert( MaxSubKeyLength <= MAX_PATH ); DWORD SubKeyNameLength = 0; WCHAR SubKeyName[MAX_PATH + 1]; // // The key has subkeys. // Find out if we are able to enumerate the key using the handle // passed as argument. // SubKeyNameLength = MAX_PATH; Error = RegEnumKey( hKeyNew, 0, SubKeyName, SubKeyNameLength ); if( Error != ERROR_SUCCESS ){ if( Error == ERROR_ACCESS_DENIED && bWriteInfo == false ){ hr = WriteObjectSecurity( hkeyOld, si, pSD ); if( hr != S_OK ) { if( m_hkeyPredefinedKey != hkeyOld ) RegCloseKey( hkeyOld ); return hr; } bWriteInfo = true; // // Handle doesn't allow KEY_QUERY_VALUE or READ_CONTROL access. // Open a new handle with these accesses. // samDesired = MAXIMUM_ALLOWED; if( si & SACL_SECURITY_INFORMATION ) { samDesired |= ACCESS_SYSTEM_SECURITY; } else if( si & DACL_SECURITY_INFORMATION ) { samDesired |= WRITE_DAC; } else if( si & OWNER_SECURITY_INFORMATION ) { samDesired |= WRITE_OWNER; } Error = RegOpenKeyEx( hkeyOld, NULL, REG_OPTION_RESERVED, samDesired, &hKeyNew ); if( Error != ERROR_SUCCESS ) { if( m_hkeyPredefinedKey != hkeyOld ) RegCloseKey( hkeyOld ); return HRESULT_FROM_WIN32(Error); } else { if( m_hkeyPredefinedKey != hkeyOld ) RegCloseKey( hkeyOld ); } SubKeyNameLength = MAX_PATH; Error = RegEnumKey( hKeyNew, 0, SubKeyName, SubKeyNameLength ); if( Error != ERROR_SUCCESS ){ RegCloseKey( hKeyNew ); return HRESULT_FROM_WIN32(Error); } } else { if( m_hkeyPredefinedKey != hKeyNew ) RegCloseKey( hKeyNew ); return HRESULT_FROM_WIN32(Error); } } } } // // Recursively apply new Owner and/or reset the ACLs // if (siLocal) { BOOL bNotAllApplied = FALSE; hr = SetSubKeysSecurity( hKeyNew, siLocal, &sdLocal, &bNotAllApplied, true ); RegFlushKey( hKeyNew ); if( m_hkeyPredefinedKey != hKeyNew ) RegCloseKey( hKeyNew ); if( bNotAllApplied ) { if( siLocal & OWNER_SECURITY_INFORMATION ) { DisplayMessage( GetInFocusHWnd(), GetModuleHandle(NULL), IDS_SET_OWNER_RECURSIVE_EX_FAIL, IDS_SECURITY ); } else if( ( siLocal & DACL_SECURITY_INFORMATION ) || ( siLocal & SACL_SECURITY_INFORMATION ) ) { DisplayMessage( GetInFocusHWnd(), GetModuleHandle(NULL), IDS_SET_SECURITY_RECURSIVE_EX_FAIL, IDS_SECURITY); } } if( hr != S_OK ) return hr; } si &= ~(SI_OWNER_RECURSE | SI_RESET_DACL_TREE | SI_RESET_SACL_TREE); //This sets the security for the top keys if (si != 0) { hr = WriteObjectSecurity( GetCompleteName(), si, pSD ); if( hr != S_OK ) { if( siLocal ) RegCloseKey( hkeyOld ); return hr; } } return hr; } STDMETHODIMP CKeySecurityInformation::WriteObjectSecurity(LPCTSTR pszObject, IN SECURITY_INFORMATION si, IN PSECURITY_DESCRIPTOR pSD ) { DWORD dwErr; SECURITY_DESCRIPTOR_CONTROL wSDControl = 0; DWORD dwRevision; PSID psidOwner = NULL; PSID psidGroup = NULL; PACL pDacl = NULL; PACL pSacl = NULL; BOOL bDefaulted; BOOL bPresent; // // Get pointers to various security descriptor parts for // calling SetNamedSecurityInfo // if( !GetSecurityDescriptorControl(pSD, &wSDControl, &dwRevision) ) { dwErr = GetLastError(); return HRESULT_FROM_WIN32(dwErr); } if( !GetSecurityDescriptorOwner(pSD, &psidOwner, &bDefaulted) ) { dwErr = GetLastError(); return HRESULT_FROM_WIN32(dwErr); } if( !GetSecurityDescriptorGroup(pSD, &psidGroup, &bDefaulted) ) { dwErr = GetLastError(); return HRESULT_FROM_WIN32(dwErr); } if( !GetSecurityDescriptorDacl(pSD, &bPresent, &pDacl, &bDefaulted) ) { dwErr = GetLastError(); return HRESULT_FROM_WIN32(dwErr); } if( !GetSecurityDescriptorSacl(pSD, &bPresent, &pSacl, &bDefaulted) ) { dwErr = GetLastError(); return HRESULT_FROM_WIN32(dwErr); } if ((si & DACL_SECURITY_INFORMATION) && (wSDControl & SE_DACL_PROTECTED)) si |= PROTECTED_DACL_SECURITY_INFORMATION; else si |= UNPROTECTED_DACL_SECURITY_INFORMATION; if ((si & SACL_SECURITY_INFORMATION) && (wSDControl & SE_SACL_PROTECTED)) si |= PROTECTED_SACL_SECURITY_INFORMATION; else si |= UNPROTECTED_SACL_SECURITY_INFORMATION; //if the selected key is predefined key, it has no parent and hence //cannot inherit any permisson from parent. //if PROTECTED_DACL_SECURITY_INFORMATION flag is not set in this case // SetSecurityInfo succeeds, but permissions are not set.[bug in SetSecurityInfo]. if ( (si & DACL_SECURITY_INFORMATION) && !m_strKeyName ) si |= PROTECTED_DACL_SECURITY_INFORMATION; else si |= UNPROTECTED_DACL_SECURITY_INFORMATION; if ( (si & SACL_SECURITY_INFORMATION) && !m_strKeyName ) si |= PROTECTED_SACL_SECURITY_INFORMATION; else si |= UNPROTECTED_SACL_SECURITY_INFORMATION; //We are on the root object if( m_strKeyName == NULL ) { dwErr = SetSecurityInfo( m_hkeyPredefinedKey, SE_REGISTRY_KEY, si, psidOwner, psidGroup, pDacl, pSacl); } else { dwErr = SetNamedSecurityInfo((LPWSTR)pszObject, SE_REGISTRY_KEY, si, psidOwner, psidGroup, pDacl, pSacl); } return (dwErr ? HRESULT_FROM_WIN32(dwErr) : S_OK); } STDMETHODIMP CKeySecurityInformation::WriteObjectSecurity(HKEY hkey, IN SECURITY_INFORMATION si, IN PSECURITY_DESCRIPTOR pSD ) { DWORD dwErr; SECURITY_DESCRIPTOR_CONTROL wSDControl = 0; DWORD dwRevision; PSID psidOwner = NULL; PSID psidGroup = NULL; PACL pDacl = NULL; PACL pSacl = NULL; BOOL bDefaulted; BOOL bPresent; // // Get pointers to various security descriptor parts for // calling SetNamedSecurityInfo // ; if( !GetSecurityDescriptorControl(pSD, &wSDControl, &dwRevision) ) { dwErr = GetLastError(); return HRESULT_FROM_WIN32(dwErr); } if( !GetSecurityDescriptorOwner(pSD, &psidOwner, &bDefaulted) ) { dwErr = GetLastError(); return HRESULT_FROM_WIN32(dwErr); } if( !GetSecurityDescriptorGroup(pSD, &psidGroup, &bDefaulted) ) { dwErr = GetLastError(); return HRESULT_FROM_WIN32(dwErr); } if( !GetSecurityDescriptorDacl(pSD, &bPresent, &pDacl, &bDefaulted) ) { dwErr = GetLastError(); return HRESULT_FROM_WIN32(dwErr); } if( !GetSecurityDescriptorSacl(pSD, &bPresent, &pSacl, &bDefaulted) ) { dwErr = GetLastError(); return HRESULT_FROM_WIN32(dwErr); } if ((si & DACL_SECURITY_INFORMATION) && (wSDControl & SE_DACL_PROTECTED)) si |= PROTECTED_DACL_SECURITY_INFORMATION; else si |= UNPROTECTED_DACL_SECURITY_INFORMATION; if ((si & SACL_SECURITY_INFORMATION) && (wSDControl & SE_SACL_PROTECTED)) si |= PROTECTED_SACL_SECURITY_INFORMATION; else si |= UNPROTECTED_SACL_SECURITY_INFORMATION; dwErr = SetSecurityInfo( hkey, SE_REGISTRY_KEY, si, psidOwner, psidGroup, pDacl, pSacl); return (dwErr ? HRESULT_FROM_WIN32(dwErr) : S_OK); } HRESULT CKeySecurityInformation::SetSubKeysSecurity( HKEY hkey, SECURITY_INFORMATION si, PSECURITY_DESCRIPTOR pSD, LPBOOL pbNotAllApplied, bool bFirstCall ) { ULONG Error; REGSAM samDesired; HRESULT hr; HRESULT hrRet; HKEY hKeyNew; //For First Call we call SetSecurityInfoEx in last if( !bFirstCall ) { SECURITY_DESCRIPTOR_CONTROL wSDControl = 0; DWORD dwRevision; PSID psidOwner = NULL; PSID psidGroup = NULL; PACL pDacl = NULL; PACL pSacl = NULL; BOOL bDefaulted; BOOL bPresent; DWORD dwErr; // // Get pointers to various security descriptor parts for // calling SetNamedSecurityInfo // if( !GetSecurityDescriptorControl(pSD, &wSDControl, &dwRevision) ) { *pbNotAllApplied = TRUE; goto SET_FOR_CHILD; } if( !GetSecurityDescriptorOwner(pSD, &psidOwner, &bDefaulted) ) { *pbNotAllApplied = TRUE; goto SET_FOR_CHILD; } if( !GetSecurityDescriptorGroup(pSD, &psidGroup, &bDefaulted) ) { *pbNotAllApplied = TRUE; goto SET_FOR_CHILD; } if( !GetSecurityDescriptorDacl(pSD, &bPresent, &pDacl, &bDefaulted) ) { *pbNotAllApplied = TRUE; goto SET_FOR_CHILD; } if( !GetSecurityDescriptorSacl(pSD, &bPresent, &pSacl, &bDefaulted) ) { *pbNotAllApplied = TRUE; goto SET_FOR_CHILD; } if ((si & DACL_SECURITY_INFORMATION) && (wSDControl & SE_DACL_PROTECTED)) si |= PROTECTED_DACL_SECURITY_INFORMATION; else si |= UNPROTECTED_DACL_SECURITY_INFORMATION; if ((si & SACL_SECURITY_INFORMATION) && (wSDControl & SE_SACL_PROTECTED)) si |= PROTECTED_SACL_SECURITY_INFORMATION; else si |= UNPROTECTED_SACL_SECURITY_INFORMATION; dwErr = SetSecurityInfo( hkey, SE_REGISTRY_KEY, si, psidOwner, psidGroup, pDacl, pSacl); if( dwErr != ERROR_SUCCESS ) { *pbNotAllApplied = TRUE; goto SET_FOR_CHILD; } } SET_FOR_CHILD: DWORD NumberOfSubKeys = 0; DWORD MaxSubKeyLength = 0; // Find out the total number of subkeys Error = RegQueryInfoKey( hkey, NULL, NULL, NULL, &NumberOfSubKeys, &MaxSubKeyLength, NULL, NULL, NULL, NULL, NULL, NULL ); if( Error != ERROR_SUCCESS ){ if( Error == ERROR_ACCESS_DENIED ) { // // Handle doesn't allow KEY_QUERY_VALUE or READ_CONTROL access. // Open a new handle with these accesses. // samDesired = KEY_QUERY_VALUE | READ_CONTROL; // MAXIMUM_ALLOWED | READ_CONTROL; if( si & SACL_SECURITY_INFORMATION ) { samDesired |= ACCESS_SYSTEM_SECURITY; } else if( si & DACL_SECURITY_INFORMATION ) { samDesired |= WRITE_DAC; } else if( si & OWNER_SECURITY_INFORMATION ) { samDesired |= WRITE_OWNER; } Error = RegOpenKeyEx( hkey, NULL, REG_OPTION_RESERVED, samDesired, &hKeyNew ); if( Error != ERROR_SUCCESS ) { *pbNotAllApplied = TRUE; return S_OK; } Error = RegQueryInfoKey( hKeyNew, NULL, NULL, NULL, &NumberOfSubKeys, &MaxSubKeyLength, NULL, NULL, NULL, NULL, NULL, NULL ); if( Error != ERROR_SUCCESS ) { RegCloseKey( hKeyNew ); *pbNotAllApplied = TRUE; return S_OK; } else RegCloseKey( hKeyNew ); } else{ *pbNotAllApplied = TRUE; return S_OK; } } if( NumberOfSubKeys == 0 ) { return S_OK; } assert( MaxSubKeyLength <= MAX_PATH ); DWORD SubKeyNameLength = 0; WCHAR SubKeyName[MAX_PATH + 1]; // // The key has subkeys. // Find out if we are able to enumerate the key using the handle // passed as argument. // SubKeyNameLength = MAX_PATH; Error = RegEnumKey( hkey, 0, SubKeyName, SubKeyNameLength ); if( Error != ERROR_SUCCESS ){ if( Error == ERROR_ACCESS_DENIED ) { // // Handle doesn't allow 'enumerate' access. // Open a new handle with KEY_ENUMERATE_SUB_KEYS access. // Error = RegOpenKeyEx( hkey, NULL, REG_OPTION_RESERVED, KEY_ENUMERATE_SUB_KEYS, // samDesired, &hKeyNew ); if( Error != ERROR_SUCCESS ){ *pbNotAllApplied = TRUE; return S_OK; } } else{ *pbNotAllApplied = TRUE; return S_OK; } } else { hKeyNew = hkey; } for( DWORD Index = 0; Index < NumberOfSubKeys; Index++ ) { // If the key has subkeys, then for each subkey, do: // - Determine the subkey name SubKeyNameLength = MAX_PATH; Error = RegEnumKey( hKeyNew, Index, SubKeyName, SubKeyNameLength ); if( Error != ERROR_SUCCESS ) { *pbNotAllApplied = TRUE; continue; //return HRESULT_FROM_WIN32( Error ); } // - Open a handle to the subkey samDesired = MAXIMUM_ALLOWED; if( si & SACL_SECURITY_INFORMATION ) samDesired |= ACCESS_SYSTEM_SECURITY; if( si & DACL_SECURITY_INFORMATION ) samDesired |= WRITE_DAC; if( si & OWNER_SECURITY_INFORMATION ) samDesired |= WRITE_OWNER; HKEY hkeyChild; Error = RegOpenKeyEx( hKeyNew, SubKeyName, REG_OPTION_RESERVED, samDesired, &hkeyChild ); if( ERROR_SUCCESS != Error ){ *pbNotAllApplied = TRUE; continue; // return HRESULT_FROM_WIN32( Error ); } // - Set the security of the child's subkeys if( S_OK != ( hr = SetSubKeysSecurity( hkeyChild, si, pSD, pbNotAllApplied, false ) ) ){ //This case will occur only if some fatal error occur which //prevents propogation on rest of the tree. if( hKeyNew != hkey ) RegCloseKey( hKeyNew ); RegCloseKey( hkeyChild ); return hr; } else{ RegCloseKey( hkeyChild ); } } //For loop if( hKeyNew != hkey ) RegCloseKey( hKeyNew ); return S_OK;; } HRESULT CKeySecurityInformation::OpenKey(IN DWORD Permission, OUT PHKEY pKey ) { LPCWSTR CompleteNameString = NULL;; ULONG Error; if( m_strKeyName == NULL){ //This is a predefined key *pKey = m_hkeyPredefinedKey; } else{ CompleteNameString = GetCompleteName1(); assert( CompleteNameString != NULL ); // Open handle to the key Error = RegOpenKeyEx(m_hkeyPredefinedKey, CompleteNameString, 0, Permission, pKey ); if( Error != ERROR_SUCCESS ) { return HRESULT_FROM_WIN32( Error ); } } if( CompleteNameString ) LocalFree( (HLOCAL) CompleteNameString); return S_OK; } OBJECT_TYPE_LIST g_DefaultOTL[] = { {0, 0, (LPGUID)&GUID_NULL}, }; BOOL SkipLocalGroup(LPCWSTR pszServerName, PSID psid) { SID_NAME_USE use; WCHAR szAccountName[MAX_PATH]; WCHAR szDomainName[MAX_PATH]; DWORD dwAccountLen = MAX_PATH; DWORD dwDomainLen = MAX_PATH; if(LookupAccountSid(pszServerName, psid, szAccountName, &dwAccountLen, szDomainName, &dwDomainLen, &use)) { if(use == SidTypeWellKnownGroup) return TRUE; } //Built In sids have first subauthority of 32 ( s-1-5-32 ) // if((*(GetSidSubAuthorityCount(psid)) >= 1 ) && (*(GetSidSubAuthority(psid,0)) == 32)) return TRUE; return FALSE; } STDMETHODIMP CKeySecurityInformation::GetEffectivePermission(const GUID* pguidObjectType, PSID pUserSid, LPCWSTR pszServerName, PSECURITY_DESCRIPTOR pSD, POBJECT_TYPE_LIST *ppObjectTypeList, ULONG *pcObjectTypeListLength, PACCESS_MASK *ppGrantedAccessList, ULONG *pcGrantedAccessListLength) { AUTHZ_RESOURCE_MANAGER_HANDLE RM = NULL; //Used for access check AUTHZ_CLIENT_CONTEXT_HANDLE CC = NULL; LUID luid = {0xdead,0xbeef}; AUTHZ_ACCESS_REQUEST AReq; AUTHZ_ACCESS_REPLY AReply; HRESULT hr = S_OK; DWORD dwFlags; AReq.ObjectTypeList = g_DefaultOTL; AReq.ObjectTypeListLength = ARRAYSIZE(g_DefaultOTL); AReply.GrantedAccessMask = NULL; AReply.Error = NULL; //Get RM if( (RM = GetAUTHZ_RM()) == NULL ) return S_FALSE; //Initialize the client context BOOL bSkipLocalGroup = SkipLocalGroup(pszServerName, pUserSid); if( !AuthzInitializeContextFromSid(bSkipLocalGroup? AUTHZ_SKIP_TOKEN_GROUPS :0, pUserSid, RM, NULL, luid, NULL, &CC) ) { return HRESULT_FROM_WIN32(GetLastError()); } //Do the Access Check AReq.DesiredAccess = MAXIMUM_ALLOWED; AReq.PrincipalSelfSid = NULL; AReq.OptionalArguments = NULL; AReply.ResultListLength = AReq.ObjectTypeListLength; AReply.SaclEvaluationResults = NULL; if( (AReply.GrantedAccessMask = (PACCESS_MASK)LocalAlloc(LPTR, sizeof(ACCESS_MASK)*AReply.ResultListLength) ) == NULL ) return E_OUTOFMEMORY; if( (AReply.Error = (PDWORD)LocalAlloc(LPTR, sizeof(DWORD)*AReply.ResultListLength)) == NULL ) { LocalFree(AReply.GrantedAccessMask); return E_OUTOFMEMORY; } if( !AuthzAccessCheck(0, CC, &AReq, NULL, pSD, NULL, 0, &AReply, NULL) ) { LocalFree(AReply.GrantedAccessMask); LocalFree(AReply.Error); return HRESULT_FROM_WIN32(GetLastError()); } if(CC) AuthzFreeContext(CC); *ppObjectTypeList = AReq.ObjectTypeList; *pcObjectTypeListLength = AReq.ObjectTypeListLength; *ppGrantedAccessList = AReply.GrantedAccessMask; *pcGrantedAccessListLength = AReq.ObjectTypeListLength; return S_OK; } STDMETHODIMP CKeySecurityInformation::GetInheritSource(SECURITY_INFORMATION si, PACL pACL, PINHERITED_FROM *ppInheritArray) { HRESULT hr = S_OK; if (NULL == m_strKeyName || !pACL || !ppInheritArray) return E_UNEXPECTED; DWORD dwErr = ERROR_SUCCESS; PINHERITED_FROM pTempInherit = NULL; PINHERITED_FROM pTempInherit2 = NULL; LPWSTR pStrTemp = NULL; LPCWSTR pszName = GetCompleteName(); pTempInherit = (PINHERITED_FROM)LocalAlloc( LPTR, sizeof(INHERITED_FROM)*pACL->AceCount); if(pTempInherit == NULL) { hr = E_OUTOFMEMORY; goto exit_gracefully; } dwErr = GetInheritanceSource((LPWSTR)pszName, SE_REGISTRY_KEY, si, TRUE, NULL, 0, pACL, NULL, &KeyMap, pTempInherit); hr = HRESULT_FROM_WIN32(dwErr); if( FAILED(hr) ) goto exit_gracefully; DWORD nSize; UINT i; nSize = sizeof(INHERITED_FROM)*pACL->AceCount; for(i = 0; i < pACL->AceCount; ++i) { if(pTempInherit[i].AncestorName) nSize += ((wcslen(pTempInherit[i].AncestorName)+1)*sizeof(WCHAR)); } pTempInherit2 = (PINHERITED_FROM)LocalAlloc( LPTR, nSize ); if(pTempInherit2 == NULL) { hr = E_OUTOFMEMORY; goto exit_gracefully; } pStrTemp = (LPWSTR)(pTempInherit2 + pACL->AceCount); for(i = 0; i < pACL->AceCount; ++i) { pTempInherit2[i].GenerationGap = pTempInherit[i].GenerationGap; if(pTempInherit[i].AncestorName) { pTempInherit2[i].AncestorName = pStrTemp; wcscpy(pStrTemp, pTempInherit[i].AncestorName); pStrTemp += (wcslen(pTempInherit[i].AncestorName)+1); } } exit_gracefully: if(SUCCEEDED(hr)) { //FreeInheritedFromArray(pTempInherit, pACL->AceCount,NULL); *ppInheritArray = pTempInherit2; } if(pTempInherit) LocalFree(pTempInherit); return hr; } /////////////////////////////////////////////////////////// // // IUnknown methods // /////////////////////////////////////////////////////////// STDMETHODIMP_(ULONG) CSecurityInformation::AddRef() { return ++m_cRef; } STDMETHODIMP_(ULONG) CSecurityInformation::Release() { if (--m_cRef == 0) { delete this; return 0; } return m_cRef; } STDMETHODIMP CSecurityInformation::QueryInterface(REFIID riid, LPVOID FAR* ppv) { // if (IsEqualIID(riid, IID_IUnknown) || IsEqualIID(riid, IID_ISecurityInformation)) if ( IsEqualIID(riid, IID_ISecurityInformation) ) { *ppv = (LPSECURITYINFO)this; m_cRef++; return S_OK; } else if(IsEqualIID(riid, IID_IEffectivePermission) ) { *ppv = (LPEFFECTIVEPERMISSION)this; m_cRef++; return S_OK; } else if(IsEqualIID(riid, IID_ISecurityObjectTypeInfo) ) { *ppv = (LPSecurityObjectTypeInfo)this; m_cRef++; return S_OK; } else { *ppv = NULL; return E_NOINTERFACE; } } HRESULT CreateSecurityInformation( IN LPCWSTR strKeyName, IN LPCWSTR strParentName, IN LPCWSTR strMachineName, IN LPCWSTR strPageTitle, IN BOOL bRemote, IN PREDEFINE_KEY PredefinedKey, IN BOOL bReadOnly, IN HWND hWnd, OUT LPSECURITYINFO *ppSi) { HRESULT hr; if( !ppSi ) return HRESULT_FROM_WIN32(ERROR_INVALID_PARAMETER); CKeySecurityInformation *ckey = new CKeySecurityInformation; if( NULL == ckey ) return HRESULT_FROM_WIN32(ERROR_NOT_ENOUGH_MEMORY); if( S_OK != ( hr = ckey->Initialize( strKeyName, strParentName, strMachineName, strPageTitle, bRemote, PredefinedKey, bReadOnly, hWnd ) ) ) { delete ckey; return hr; } else { *ppSi = ckey; return S_OK; } } //Some helper functions BOOL DisplayMessage( HWND hWnd, HINSTANCE hInstance, DWORD dwMessageId, DWORD dwCaptionId ) { WCHAR pszMessage[1025]; WCHAR pszTitle[1025]; LPWSTR lpTitle = NULL; if( !LoadString(hInstance, dwMessageId, pszMessage, 1024 ) ) return FALSE; if( dwCaptionId ) { if( LoadString(hInstance, dwCaptionId, pszTitle, 1024 ) ) lpTitle = pszTitle; } // Display the string. MessageBox( hWnd, (LPCTSTR)pszMessage, (LPCTSTR)lpTitle, MB_OK | MB_ICONINFORMATION |MB_APPLMODAL ); return TRUE; }