//+--------------------------------------------------------------------------- // // Microsoft Windows // Copyright (C) Microsoft Corporation, 1993 - 1994. // // File: registry.cxx // // Contents: local functions // // History: 8/94 davemont Created // //---------------------------------------------------------------------------- #include #pragma hdrstop #include #include // // Registry generic mapping // GENERIC_MAPPING gRegGenMapping = {STANDARD_RIGHTS_READ | 0x1, STANDARD_RIGHTS_WRITE | 0x2, STANDARD_RIGHTS_EXECUTE | 0x4, STANDARD_RIGHTS_REQUIRED | 0x3F}; //+--------------------------------------------------------------------------- // // Function : GetDesiredAccess // // Synopsis : Gets the access required to open object to be able to set or // get the specified security info. // // Arguments: IN [SecurityOpenType] - Flag indicating if the object is to be // opened to read or write the DACL // //---------------------------------------------------------------------------- ACCESS_MASK RegGetDesiredAccess(IN SECURITY_OPEN_TYPE OpenType, IN SECURITY_INFORMATION SecurityInfo) { acDebugOut((DEB_TRACE_ACC, "in GetDesiredAccess \n")); ACCESS_MASK DesiredAccess = 0; if ( (SecurityInfo & OWNER_SECURITY_INFORMATION) || (SecurityInfo & GROUP_SECURITY_INFORMATION) ) { switch (OpenType) { case READ_ACCESS_RIGHTS: DesiredAccess |= READ_CONTROL; break; case WRITE_ACCESS_RIGHTS: DesiredAccess |= WRITE_OWNER; break; case MODIFY_ACCESS_RIGHTS: DesiredAccess |= READ_CONTROL | WRITE_OWNER; break; } } if (SecurityInfo & DACL_SECURITY_INFORMATION) { switch (OpenType) { case READ_ACCESS_RIGHTS: DesiredAccess |= READ_CONTROL; break; case WRITE_ACCESS_RIGHTS: DesiredAccess |= WRITE_DAC; break; case MODIFY_ACCESS_RIGHTS: DesiredAccess |= READ_CONTROL | WRITE_DAC; break; } } if (SecurityInfo & SACL_SECURITY_INFORMATION) { DesiredAccess |= READ_CONTROL | ACCESS_SYSTEM_SECURITY; } acDebugOut((DEB_TRACE_ACC, "out RegGetDesiredAccess: %lu\n", DesiredAccess)); return (DesiredAccess); } //+--------------------------------------------------------------------------- // // Function: OpenRegistryObject // // Synopsis: Opens the specified registry object // // Arguments: [IN pwszRegistry] -- The name of the registry key // to open // [IN AccessMask] -- Flags indicating if the object // is to be opened to read or write // the DACL // [OUT pHandle] -- Where the open handle is // returned // // Returns: ERROR_SUCCESS -- Success // ERROR_NOT_ENOUGH_MEMORY -- A memory allocation failed // ERROR_INVALID_PARAMETER -- A bad name was given // //---------------------------------------------------------------------------- DWORD OpenRegistryObject(IN LPWSTR pwszRegistry, IN ACCESS_MASK AccessMask, OUT PHANDLE pHandle) { acDebugOut((DEB_TRACE, "in OpenRegistryObject\n")); DWORD dwErr; HKEY hBase; if(pwszRegistry != NULL) { WCHAR wszName[MAX_PATH + 1]; PWSTR pwszName; // // save the object since we must crack it to go to remote machines // dwErr = AccGetBufferOfSizeW(pwszRegistry, wszName, &pwszName); if(dwErr == ERROR_SUCCESS) { PWSTR pwszRemaining, pwszMachine; // // Separate the names // dwErr = ParseName(pwszName, &pwszMachine, &pwszRemaining); if(dwErr == ERROR_SUCCESS) { PWSTR pwszKey = NULL; // // look for the key names localization required. // if (pwszRemaining != NULL) { PWSTR pwszBase = pwszRemaining; pwszKey = wcschr(pwszRemaining, L'\\'); if(pwszKey != NULL) { *pwszKey = L'\0'; pwszKey++; } // // Now, figure out what our base key will be // if(_wcsicmp(pwszBase, L"MACHINE") == 0) { hBase = HKEY_LOCAL_MACHINE; } else if(_wcsicmp(pwszBase, L"USERS") == 0 || _wcsicmp(pwszBase, L"USER") == 0 ) { hBase = HKEY_USERS; } // // The next three are valid only for the local machine // else if(pwszMachine == NULL && _wcsicmp(pwszBase, L"CLASSES_ROOT") == 0) { hBase = HKEY_CLASSES_ROOT; } else if(pwszMachine == NULL && _wcsicmp(pwszBase,L"CURRENT_USER") == 0) { hBase = HKEY_CURRENT_USER; } else if(pwszMachine == NULL && _wcsicmp(pwszBase, L"CONFIG") == 0) { hBase = HKEY_CURRENT_CONFIG; } else { dwErr = ERROR_INVALID_PARAMETER; } } else { dwErr = ERROR_INVALID_PARAMETER; } if(dwErr == ERROR_SUCCESS) { // // if it is a remote name, connect to that registry // if(pwszMachine != NULL) { HKEY hMach = hBase; dwErr = RegConnectRegistry(pwszMachine, hMach, &hBase); } // // Now, open the key // if(dwErr == ERROR_SUCCESS) { dwErr = RegOpenKeyEx(hBase, pwszKey, 0, AccessMask, (PHKEY)pHandle); if(pwszMachine != NULL) { RegCloseKey(hBase); } } } } AccFreeBufferOfSizeW(wszName, pwszName); } } else { dwErr = ERROR_INVALID_PARAMETER; } acDebugOut((DEB_TRACE, "Out OpenRegistryObject: %lu\n", dwErr)); return(dwErr); } //+--------------------------------------------------------------------------- // // Function: ReadRegistryPropertyRights // // Synopsis: Gets the specified security info for the specified registry // object // // Arguments: [IN pwszRegistry] -- The reg key to get the rights // for // [IN pRightsList] -- SecurityInfo to read based // on properties // [IN cRights] -- Number of items in rights list // [IN AccessList] -- Access List to fill in // // Returns: ERROR_SUCCESS -- Success // ERROR_INVALID_PARAMETER -- A bad property was encountered // ERROR_NOT_ENOUGH_MEMORY -- A memory allocation failed // //---------------------------------------------------------------------------- DWORD ReadRegistryPropertyRights(IN LPWSTR pwszRegistry, IN PACTRL_RIGHTS_INFO pRightsList, IN ULONG cRights, IN CAccessList& AccessList) { acDebugOut((DEB_TRACE, "in ReadRegistryPropertyRights\n")); HANDLE hReg; DWORD dwErr; // // Currently, there are only registry object properties // ASSERT(cRights == 1 && pRightsList[0].pwszProperty == NULL); if(cRights != 1 || pRightsList[0].pwszProperty != NULL) { return(ERROR_INVALID_PARAMETER); } // // Set the lookup server name // dwErr = SetAccessListLookupServer( pwszRegistry, AccessList ); // // Open the registry key // if(dwErr == ERROR_SUCCESS) { dwErr = OpenRegistryObject(pwszRegistry, RegGetDesiredAccess(READ_ACCESS_RIGHTS, pRightsList[0].SeInfo), &hReg); } if(dwErr == ERROR_SUCCESS) { PSECURITY_DESCRIPTOR pSD; dwErr = ReadRegistrySecurityInfo(hReg, pRightsList[0].SeInfo, &pSD); // // If that worked, we'll have to get the parent SD, if it exists, // and see if we can determine the inheritance on our current object // if(dwErr == ERROR_SUCCESS) { if((pRightsList[0].SeInfo & ~(OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION)) != 0 && !FLAG_ON(((PISECURITY_DESCRIPTOR)pSD)->Control, SE_SACL_AUTO_INHERITED | SE_DACL_AUTO_INHERITED)) { // // Ok, it's downlevel, so get the parent SD... // PSECURITY_DESCRIPTOR pParentSD; dwErr = GetRegistryParentRights(pwszRegistry, pRightsList[0].SeInfo, &pParentSD); // // Also, the routine to convert from nt4 to nt5 security // descriptor requires that we have the owner and group, // so we may have to reread the child SD if we don't have // that info // if(dwErr == ERROR_SUCCESS && (!FLAG_ON(pRightsList[0].SeInfo, OWNER_SECURITY_INFORMATION) || !FLAG_ON(pRightsList[0].SeInfo, GROUP_SECURITY_INFORMATION))) { AccFree(pSD); pSD = NULL; dwErr = ReadRegistrySecurityInfo(hReg, pRightsList[0].SeInfo | OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION, &pSD); } // // A NULL parent SD means this object has no parent! // if(dwErr == ERROR_SUCCESS && pParentSD != NULL) { PSECURITY_DESCRIPTOR pNewSD; dwErr = ConvertToAutoInheritSD(pParentSD, pSD, TRUE, &gRegGenMapping, &pNewSD); if(dwErr == ERROR_SUCCESS) { dwErr = AccessList.AddSD(pNewSD, pRightsList[0].SeInfo, pRightsList[0].pwszProperty); DestroyPrivateObjectSecurity(&pNewSD); } AccFree(pParentSD); } } else { // // Simply add the SD to our list // dwErr = AccessList.AddSD(pSD, pRightsList[0].SeInfo, pRightsList[0].pwszProperty); } // // Make sure to free the security descriptor... // AccFree(pSD); } RegCloseKey((HKEY)hReg); } acDebugOut((DEB_TRACE, "Out ReadRegistryPropertyRights: %lu\n", dwErr)); return(dwErr); } //+--------------------------------------------------------------------------- // // Function: ReadRegistryRights // // Synopsis: Gets the specified security info for the specified registry // object // // Arguments: [IN hRegistry] -- Reg handle to get the rights // for // [IN pRightsList] -- SecurityInfo to read based // on properties // [IN cRights] -- Number of items in rights list // [IN AccessList] -- Access List to fill in // // Returns: ERROR_SUCCESS -- Success // ERROR_INVALID_PARAMETER -- A bad property was encountered // ERROR_NOT_ENOUGH_MEMORY -- A memory allocation failed // //---------------------------------------------------------------------------- DWORD ReadRegistryRights(IN HANDLE hRegistry, IN PACTRL_RIGHTS_INFO pRightsList, IN ULONG cRights, IN CAccessList& AccessList) { acDebugOut((DEB_TRACE, "in ReadRegistryRights\n")); DWORD dwErr; // // Currently, there are only registry object properties // ASSERT(cRights == 1 && pRightsList[0].pwszProperty == NULL); if(cRights != 1 || pRightsList[0].pwszProperty != NULL) { return(ERROR_INVALID_PARAMETER); } PSECURITY_DESCRIPTOR pSD = NULL; dwErr = ReadRegistrySecurityInfo(hRegistry, pRightsList[0].SeInfo, &pSD); if((dwErr != ERROR_SUCCESS) || (pSD == NULL)) { return(dwErr); } // // Take a look at it... If it's a downlevel object, let's reread it as an uplevel, if // possible // if(!FLAG_ON(((PISECURITY_DESCRIPTOR)pSD)->Control, SE_SACL_AUTO_INHERITED | SE_DACL_AUTO_INHERITED)) { PWSTR pwszRegPath = NULL; dwErr = ConvertRegHandleToName((HKEY)hRegistry, &pwszRegPath); if(dwErr != ERROR_SUCCESS) { if(dwErr == ERROR_INVALID_HANDLE) { // // It's remote, so add it as is... // dwErr = AccessList.AddSD(pSD, pRightsList->SeInfo, pRightsList->pwszProperty); } } else { dwErr = ReadRegistryPropertyRights(pwszRegPath, pRightsList, cRights, AccessList); AccFree(pwszRegPath); } } else { // // It's already uplevel, so add it as is... // dwErr = AccessList.AddSD(pSD, pRightsList->SeInfo, pRightsList->pwszProperty); } AccFree(pSD); acDebugOut((DEB_TRACE, "Out ReadRegistryRights: %lu\n", dwErr)); return(dwErr); } //+--------------------------------------------------------------------------- // // Function: ReadRegistrySecurityInfo // // Synopsis: Reads the specified security info for the handle's registry // key object // // Arguments: [IN hRegistry] -- The handle to the object to // get the rights for // [IN SeInfo] -- SecurityInfo to read based // [OUT ppOwner] -- The owner sid // [OUT ppGroup] -- The group sid // [OUT pDAcl] -- The DACL // [OUT pSAcl] -- The SACL // [OUT pSD] -- The security descriptor itself // // Returns: ERROR_SUCCESS -- Success // ERROR_NOT_ENOUGH_MEMORY -- A memory allocation failed // //---------------------------------------------------------------------------- DWORD ReadRegistrySecurityInfo(IN HANDLE hRegistry, IN SECURITY_INFORMATION SeInfo, OUT PSECURITY_DESCRIPTOR *ppSD) { acDebugOut((DEB_TRACE, "in ReadRegistrySecurityInfo \n")); ULONG cSize = 0; DWORD dwErr; // // First, get the size we need // dwErr = RegGetKeySecurity((HKEY)hRegistry, SeInfo, *ppSD, &cSize); if(dwErr == ERROR_INSUFFICIENT_BUFFER) { dwErr = ERROR_SUCCESS; *ppSD = (PISECURITY_DESCRIPTOR)AccAlloc(cSize); if(*ppSD == NULL) { dwErr = ERROR_NOT_ENOUGH_MEMORY; } else { dwErr = RegGetKeySecurity((HKEY)hRegistry, SeInfo, *ppSD, &cSize); if(dwErr == ERROR_SUCCESS && FLAG_ON(((SECURITY_DESCRIPTOR *)*ppSD)->Control,SE_SELF_RELATIVE)) { PSECURITY_DESCRIPTOR pAbs; dwErr = MakeSDAbsolute(*ppSD, SeInfo, &pAbs); if(dwErr == ERROR_SUCCESS) { AccFree(*ppSD); *ppSD = pAbs; } } } } else { ASSERT(dwErr != ERROR_INSUFFICIENT_BUFFER); } acDebugOut((DEB_TRACE, "Out ReadRegistrySecurityInfo: %lu\n", dwErr)); return(dwErr); } //+--------------------------------------------------------------------------- // // Function: GetRegistryParentRights // // Synopsis: Determines who the parent is, and gets the access rights // for it. It is used to aid in determining what the approriate // inheritance bits are. // // This operation does not make sense for kernel objects // // Arguments: [IN pwszRegistry] -- The reg path to get the parent // for // [IN SeInfo] -- The security information to do // the read for // [OUT ppDAcl] -- Where the DACL is returned // [OUT ppSAcl] -- Where the SACL is returned // [OUT ppSD] -- Where the Security Descriptor // is returned // // Returns: ERROR_SUCCESS -- Success // // //---------------------------------------------------------------------------- DWORD GetRegistryParentRights(IN LPWSTR pwszRegistry, IN SECURITY_INFORMATION SeInfo, OUT PSECURITY_DESCRIPTOR *ppSD) { DWORD dwErr = ERROR_SUCCESS; // // Basically, we'll figure out who our parent is, and get their info // PWSTR pwszLastComp = wcsrchr(pwszRegistry, L'\\'); if(pwszLastComp == NULL) { // // Ok, we must be at the root, so we won't have any inheritance // // // Return success after nulling out SD. // *ppSD = NULL; } else { // // We'll shorten our path, and then get the info // *pwszLastComp = L'\0'; HANDLE hReg; // // Don't want owner or group // SeInfo &= ~(OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION); dwErr = OpenRegistryObject(pwszRegistry, RegGetDesiredAccess(READ_ACCESS_RIGHTS,SeInfo), &hReg); if(dwErr == ERROR_SUCCESS) { dwErr = ReadRegistrySecurityInfo(hReg, SeInfo, ppSD); RegCloseKey((HKEY)hReg); } *pwszLastComp = L'\\'; } return(dwErr); } //+--------------------------------------------------------------------------- // // Function: SetRegistrySecurityInfo // // Synopsis: Sets the specified security info on the specified registry // object // // Arguments: [IN hService] -- The handle of the object // [IN SeInfo] -- Flag indicating what security // info to set // [IN pwszProperty] -- The property on the object to // set // For kernel objects, this MBZ // [IN pSD] -- The security descriptor to set // // Returns: ERROR_SUCCESS -- Success // ERROR_INVALID_PARAMETER -- A bad property was given // //---------------------------------------------------------------------------- DWORD SetRegistrySecurityInfo(IN HANDLE hRegistry, IN SECURITY_INFORMATION SeInfo, IN PWSTR pwszProperty, IN PSECURITY_DESCRIPTOR pSD) { acDebugOut((DEB_TRACE, "in SetNamedRegistrySecurityInfo\n")); DWORD dwErr; // // Registry keys don't have properties // if(pwszProperty != NULL) { dwErr = ERROR_INVALID_PARAMETER; } else { // // Marta only writes uplevel security descriptors. // // The caller of SetRegistrySecurityInfo will call with SE_xACL_AUTO_INHERITED off in those // cases that it wants the underlying registry to do auto inheritance. // The caller of SetRegistrySecurityInfo will call with SE_xACL_AUTO_INHERITED on in those // cases that it wants the underlying registry to simply store the bits. // // In the later case, the OS uses the SE_xACL_AUTO_INHERIT_REQ bit as a flag indicating // that it is OK to preserve SE_xACL_AUTO_INHERITED bit. // if(FLAG_ON(SeInfo, DACL_SECURITY_INFORMATION)) { ((PISECURITY_DESCRIPTOR)pSD)->Control |= SE_DACL_AUTO_INHERIT_REQ; } if(FLAG_ON(SeInfo, SACL_SECURITY_INFORMATION)) { ((PISECURITY_DESCRIPTOR)pSD)->Control |= SE_SACL_AUTO_INHERIT_REQ; } dwErr = RegSetKeySecurity((HKEY)hRegistry, SeInfo, pSD); } acDebugOut((DEB_TRACE, "Out SetRegistrySecurityInfo: %lu\n", dwErr)); return(dwErr); } #define CLEANUP_ON_INTERRUPT(pstopflag) \ if(*pstopflag != 0) \ { \ goto RegCleanup; \ } //+--------------------------------------------------------------------------- // // Function: SetAndPropagateRegistryPropertyRights // // Synopsis: Sets the access on the given registry path and propagates // it as necessary // // Arguments: [IN pwszRegistry] -- The path to set and propagate // [IN pwszProperty] -- The registry property to // operate upon // [IN RootAccList] -- The CAccessList class that has // the security descriptor/info // [IN pfStopFlag] -- Address of the stop flag // to be monitored // [IN pcProcessed] -- count of processed items to // be incremented. // // Returns: ERROR_SUCCESS -- Success // ERROR_INVALID_PARAMETER -- A bad paramter was given // //---------------------------------------------------------------------------- DWORD SetAndPropagateRegistryPropertyRights(IN PWSTR pwszRegistry, IN PWSTR pwszProperty, IN CAccessList& RootAccList, IN PULONG pfStopFlag, IN PULONG pcProcessed) { acDebugOut((DEB_TRACE, "in SetAndPropagateRegistryPropertyRights\n")); DWORD dwErr = ERROR_SUCCESS; // // First, get our security descriptor and sec info // HKEY hReg = NULL; PSECURITY_DESCRIPTOR pSD = NULL; PSECURITY_DESCRIPTOR pParentSD = NULL; SECURITY_INFORMATION SeInfo = 0; dwErr = RootAccList.BuildSDForAccessList(&pSD, &SeInfo, ACCLIST_SD_ABSOK); if(dwErr == ERROR_SUCCESS) { // // Next, open the registry // dwErr = OpenRegistryObject(pwszRegistry, RegGetDesiredAccess(MODIFY_ACCESS_RIGHTS, SeInfo) | KEY_ENUMERATE_SUB_KEYS | KEY_QUERY_VALUE, (PHANDLE)&hReg); if(dwErr == ERROR_SUCCESS) { // // Next, get our parent security descriptor // // // If we are only setting the owner or group, we don't need to get the parent // if (FLAG_ON(SeInfo, DACL_SECURITY_INFORMATION) || FLAG_ON(SeInfo, SACL_SECURITY_INFORMATION) ) { dwErr = GetRegistryParentRights(pwszRegistry, SeInfo, &pParentSD); } if(dwErr == ERROR_SUCCESS) { // // Make the call // dwErr = SetAndPropRegRights(hReg, pwszRegistry, SeInfo, pParentSD, pSD, pfStopFlag, pcProcessed); } } } // // Clean up // if(hReg != NULL) { RegCloseKey(hReg); } AccFree(pParentSD); acDebugOut((DEB_TRACE, "Out SetAndPropagateRegistryPropertyRights: %ld\n", dwErr)); return(dwErr); } //+--------------------------------------------------------------------------- // // Function: SetAndPropagateRegistryPropertyRightsByHandle // // Synopsis: Same as above, but assumes the registry key has already // been opened // // Arguments: [IN hReg] -- The registry key to use // [IN RootAccList] -- The CAccessList class that has // the security descriptor/info // [IN pfStopFlag] -- Address of the stop flag // to be monitored // [IN pcProcessed] -- count of processed items to // be incremented. // // Returns: ERROR_SUCCESS -- Success // ERROR_INVALID_PARAMETER -- A bad paramter was given // //---------------------------------------------------------------------------- DWORD SetAndPropagateRegistryPropertyRightsByHandle(IN HKEY hReg, IN CAccessList& RootAccList, IN PULONG pfStopFlag, IN PULONG pcProcessed) { acDebugOut((DEB_TRACE, "in SetAndPropagateRegistryPropertyRightsByHandle\n")); DWORD dwErr = ERROR_SUCCESS; PSECURITY_DESCRIPTOR pParentSD = NULL; HANDLE hObject = NULL; BOOL fUplevelAcl = TRUE; PWSTR pwszRegPath = NULL; // // First, get our security descriptor and sec info // PSECURITY_DESCRIPTOR pSD = NULL; SECURITY_INFORMATION SeInfo = 0; dwErr = RootAccList.BuildSDForAccessList(&pSD, &SeInfo, ACCLIST_SD_ABSOK); if(dwErr == ERROR_SUCCESS) { // // If we are only setting the owner or group, we don't need to get the parent // if (FLAG_ON(SeInfo, DACL_SECURITY_INFORMATION) || FLAG_ON(SeInfo, SACL_SECURITY_INFORMATION) ) { dwErr = ConvertRegHandleToName(hReg, &pwszRegPath); if((dwErr != ERROR_SUCCESS) || (pwszRegPath == NULL)) { if(dwErr == ERROR_INVALID_HANDLE) { dwErr = ERROR_SUCCESS; } } else { dwErr = GetRegistryParentRights(pwszRegPath, SeInfo, &pParentSD); } } if(dwErr == ERROR_SUCCESS) { // // Make the call // dwErr = SetAndPropRegRights(hReg, NULL, SeInfo, pParentSD, pSD, pfStopFlag, pcProcessed); if(dwErr == ERROR_ACCESS_DENIED) { // // See if we can reopen the path adding in readcontrol, and try it all again // if(pwszRegPath == NULL) { dwErr = ConvertRegHandleToName(hReg, &pwszRegPath); } if(pwszRegPath != NULL) { dwErr = SetAndPropagateRegistryPropertyRights(pwszRegPath, NULL, RootAccList, pfStopFlag, pcProcessed); } } } } // // Clean up // AccFree(pwszRegPath); AccFree(pParentSD); acDebugOut((DEB_TRACE, "Out SetAndPropagateRegistryPropertyRightsByHandle: %ld\n", dwErr)); return(dwErr); } //+--------------------------------------------------------------------------- // // Function: PropagateRegRightsDeep, recursive // // Synopsis: Does a deep propagation of the access. At the same time, it // will update NT4 acls to NT5 acls. This function is only // called on downlevel registries, so the update will always // happen (where appropriate). The algorithm is: // - Read the current security descriptor from the object // - If it's a downlevel acl, update it using the OLD // parent security descriptor (to set any inheritied aces) // - Update the security descriptor using the NEW parent // security descriptor. // - Repeat for its children. (This is necessar, since there // could have been unmarked inheritance off of the old // security descriptor) // // Arguments: [IN pOldParentSD] -- The previous parent SD (before // the current parent SD was // stamped on the object) // [IN pParentSD] -- The current parent sd // [IN SeInfo] -- What is being written // [IN hParent] -- Opened parent registry key // [IN pcProcessed] -- Where the number processed is // returned. // [IN pfStopFlag] -- Stop flag to monitor // [IN fProtectedFlag] -- Determines whether the acls are already // protected // [IN hProcessToken] -- Handle to the process token // [IN LogList] -- List of keys to which propagation failed // // Returns: ERROR_SUCCESS -- Success // ERROR_INVALID_PARAMETER -- A bad paramter was given // //---------------------------------------------------------------------------- DWORD PropagateRegRightsDeep(IN PSECURITY_DESCRIPTOR pOldParentSD, IN PSECURITY_DESCRIPTOR pParentSD, IN SECURITY_INFORMATION SeInfo, IN HKEY hParent, IN PULONG pcProcessed, IN PULONG pfStopFlag, IN ULONG fProtectedFlag, IN HANDLE hProcessToken, IN OUT CSList& LogList) { acDebugOut((DEB_TRACE, "in PropagteRegRightsDeep\n")); DWORD dwErr = ERROR_SUCCESS; SECURITY_DESCRIPTOR *pChildSD = NULL; PSECURITY_DESCRIPTOR pNewSD = NULL; BOOL fUpdateChild = FALSE; // Write out the child? BOOL fAccFreeChild = TRUE; // How to free the child // // Check to see if we've reached full protection saturation // if(fProtectedFlag == (SE_DACL_PROTECTED | SE_SACL_PROTECTED)) { acDebugOut((DEB_TRACE_PROP, "Parent is fully or effectively protected\n")); return(ERROR_SUCCESS); } HKEY hChild = NULL; ULONG cSubKeys; dwErr = RegQueryInfoKey(hParent, NULL, NULL, NULL, &cSubKeys, NULL, NULL, NULL, NULL, NULL, NULL, NULL); CLEANUP_ON_INTERRUPT(pfStopFlag); if(dwErr == ERROR_SUCCESS && cSubKeys != 0) { WCHAR wszBuff[MAX_PATH + 1]; ULONG iIndex = 0; ULONG cSize; FILETIME WriteTime; while(dwErr == ERROR_SUCCESS) { cSize = MAX_PATH + 1; dwErr = RegEnumKeyEx(hParent, iIndex, wszBuff, &cSize, NULL, NULL, NULL, &WriteTime); if(dwErr == ERROR_NO_MORE_ITEMS) { dwErr = ERROR_SUCCESS; break; } acDebugOut((DEB_TRACE_PROP,"Propagating to %ws\n", wszBuff)); CLEANUP_ON_INTERRUPT(pfStopFlag); // // Now, determine if we need to propagate or not... // if(dwErr == ERROR_SUCCESS) { ULONG cSDLen = 0; BOOL fWriteSD = FALSE; dwErr = RegOpenKeyEx(hParent, wszBuff, 0, RegGetDesiredAccess(MODIFY_ACCESS_RIGHTS, SeInfo) | KEY_ENUMERATE_SUB_KEYS | KEY_QUERY_VALUE, &hChild); if(dwErr == ERROR_SUCCESS) { // // Get our number of children // dwErr = RegQueryInfoKey(hChild, NULL, NULL, NULL, &cSubKeys, NULL, NULL, NULL, NULL, NULL, NULL, NULL); if ( dwErr == ERROR_INSUFFICIENT_BUFFER ) { acDebugOut((DEB_ERROR,"RegQueryInfoKey failure on %ws\n", wszBuff)); } if(dwErr == ERROR_SUCCESS) { CLEANUP_ON_INTERRUPT(pfStopFlag); // // Read the current security descriptor // dwErr = ReadRegistrySecurityInfo(hChild, SeInfo, (PSECURITY_DESCRIPTOR *)&pChildSD); CLEANUP_ON_INTERRUPT(pfStopFlag); if(dwErr == ERROR_SUCCESS && !(FLAG_ON(pChildSD->Control, SE_DACL_AUTO_INHERITED | SE_SACL_AUTO_INHERITED))) { // // Before we convert this, we may need to reread the SD... if // we don't have owner and group // if(!FLAG_ON(SeInfo, OWNER_SECURITY_INFORMATION) || !FLAG_ON(SeInfo, GROUP_SECURITY_INFORMATION)) { AccFree(pChildSD); pChildSD = NULL; dwErr = ReadRegistrySecurityInfo( hChild, SeInfo | OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION, (PSECURITY_DESCRIPTOR *)&pChildSD); if(dwErr == ERROR_ACCESS_DENIED) { RegCloseKey(hChild); dwErr = RegOpenKeyEx(hParent, wszBuff, 0, RegGetDesiredAccess(MODIFY_ACCESS_RIGHTS, SeInfo | OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION) | KEY_ENUMERATE_SUB_KEYS | KEY_QUERY_VALUE, &hChild); if(dwErr == ERROR_SUCCESS) { dwErr = ReadRegistrySecurityInfo( hChild, SeInfo | OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION, (PSECURITY_DESCRIPTOR *)&pChildSD); } } } if(dwErr == ERROR_SUCCESS) { dwErr = ConvertToAutoInheritSD(pOldParentSD, pChildSD, TRUE, &gRegGenMapping, &pNewSD); AccFree(pChildSD); if(dwErr == ERROR_SUCCESS) { pChildSD = (SECURITY_DESCRIPTOR *)pNewSD; fAccFreeChild = FALSE; pNewSD = NULL; } } } // // Now, compute the new security descriptor if(dwErr == ERROR_SUCCESS) { DebugDumpSD("CPOS ParentSD", pParentSD); DebugDumpSD("CPOS CreatorSD", pChildSD); if(CreatePrivateObjectSecurityEx(pParentSD, pChildSD, &pNewSD, NULL, TRUE, SEF_DACL_AUTO_INHERIT | SEF_SACL_AUTO_INHERIT | SEF_AVOID_OWNER_CHECK | SEF_AVOID_PRIVILEGE_CHECK, hProcessToken, &gRegGenMapping) == FALSE) { dwErr = GetLastError(); } } #ifdef DBG else { DebugDumpSD("CPOS NewChild", pNewSD); } #endif if(dwErr == ERROR_SUCCESS) { // // If the resultant child is protected, don't bother propagating // down. // if(FLAG_ON(SeInfo, DACL_SECURITY_INFORMATION)) { if(DACL_PROTECTED(pNewSD)) { fProtectedFlag |= SE_DACL_PROTECTED; } } if(FLAG_ON(SeInfo, SACL_SECURITY_INFORMATION)) { if(SACL_PROTECTED(pNewSD)) { fProtectedFlag |= SE_SACL_PROTECTED; } } if(FLAG_ON( fProtectedFlag, (SE_DACL_PROTECTED | SE_SACL_PROTECTED))) { cSubKeys = 0; dwErr = InsertPropagationFailureEntry(LogList, 0, fProtectedFlag, wszBuff); } // // If we haven't changed the acl, security descriptor, then // we can also quit // if(EqualSecurityDescriptors(pNewSD, pChildSD)) { cSubKeys = 0; } } } // // Now, if it's a directory, call ourselves // if(dwErr == ERROR_SUCCESS && cSubKeys != 0) { dwErr = PropagateRegRightsDeep(pChildSD, pNewSD, SeInfo, hChild, pcProcessed, pfStopFlag, fProtectedFlag, hProcessToken, LogList); if(dwErr == ERROR_ACCESS_DENIED) { dwErr = InsertPropagationFailureEntry(LogList, dwErr, 0, wszBuff); } } // // Free the old child, since we won't need it anymore // if(fAccFreeChild == TRUE) { AccFree(pChildSD); } else { DestroyPrivateObjectSecurity((PSECURITY_DESCRIPTOR *) &pChildSD); } pChildSD = NULL; } } acDebugOut((DEB_TRACE_PROP, "Processed %ws: %lu\n", wszBuff, dwErr)); // // Finally, set the new security // if(dwErr == ERROR_SUCCESS) { // // Now, we'll simply stamp it on the object // dwErr = SetRegistrySecurityInfo(hChild, SeInfo, NULL, pNewSD); (*pcProcessed)++; } DestroyPrivateObjectSecurity(&pNewSD); pNewSD = NULL; CLEANUP_ON_INTERRUPT(pfStopFlag); iIndex++; } } if(dwErr == ERROR_NO_MORE_FILES) { dwErr = ERROR_SUCCESS; } RegCleanup: if(hChild != NULL) { RegCloseKey(hChild); } if(pNewSD != NULL) { DestroyPrivateObjectSecurity(&pNewSD); } acDebugOut((DEB_TRACE, "Out PropagteRegRightsDeep: %ld\n", dwErr)); return(dwErr); } //+--------------------------------------------------------------------------- // // Function: SetAndPropRegRights // // Synopsis: Sets the access on the given registry path and propagates // it as necessary // // Arguments: [IN hReg] -- Handle to the reg. object to set // [IN pwszPath] -- Registry path referred to by hReg, // if known // [IN SeInfo] -- Security information to set // [IN pParentSD] -- Security descriptor of the parent // [IN pSD] -- SD to set // [IN pfStopFlag] -- Address of the stop flag // to be monitored // [IN pcProcessed] -- count of processed items to // be incremented. // // Returns: ERROR_SUCCESS -- Success // //---------------------------------------------------------------------------- DWORD SetAndPropRegRights(IN HKEY hReg, IN PWSTR pwszPath, IN SECURITY_INFORMATION SeInfo, IN PSECURITY_DESCRIPTOR pParentSD, IN PSECURITY_DESCRIPTOR pSD, IN PULONG pfStopFlag, IN PULONG pcProcessed) { acDebugOut((DEB_TRACE, "in SetAndPropRegRights\n")); DWORD dwErr = ERROR_SUCCESS; PSECURITY_DESCRIPTOR pOldObjSD = NULL; PSECURITY_DESCRIPTOR pUpdatedSD = NULL; PSECURITY_DESCRIPTOR pVerifySD = NULL; BOOL fManualProp = FALSE; ULONG fProtected = 0; ULONG cSubKeys; HANDLE hProcessToken = NULL; PSID pOwner = NULL, pGroup = NULL; CSList FailureLogList(FreePropagationFailureListEntry); // // Ok, read the existing security // dwErr = ReadRegistrySecurityInfo(hReg, SeInfo, &pOldObjSD); // // Now, we'll write out the current, and then read it back and make sure // that it's properly updated // if(dwErr == ERROR_SUCCESS && FLAG_ON(SeInfo, DACL_SECURITY_INFORMATION | SACL_SECURITY_INFORMATION) ) { CLEANUP_ON_INTERRUPT(pfStopFlag); dwErr = SetRegistrySecurityInfo(hReg, SeInfo, NULL, pSD); if(dwErr == ERROR_SUCCESS) { (*pcProcessed)++; CLEANUP_ON_INTERRUPT(pfStopFlag); dwErr = ReadRegistrySecurityInfo(hReg, SeInfo, &pVerifySD); if(dwErr == ERROR_SUCCESS) { // // Check to see if this was done uplevel... // PISECURITY_DESCRIPTOR pISD = (PISECURITY_DESCRIPTOR)pVerifySD; if(!(FLAG_ON(SeInfo, DACL_SECURITY_INFORMATION) && FLAG_ON(pISD->Control, SE_DACL_AUTO_INHERITED)) && !(FLAG_ON(SeInfo, SACL_SECURITY_INFORMATION) && FLAG_ON(pISD->Control, SE_SACL_AUTO_INHERITED))) { // // It's not uplevel, so we'll turn the AutoInherit // flags on, rewrite it, and do our own propagation, // only if this is a container and we're setting the // dacl or sacl // if(FLAG_ON(SeInfo, (DACL_SECURITY_INFORMATION | SACL_SECURITY_INFORMATION))) { fManualProp = TRUE; } if(FLAG_ON(SeInfo, DACL_SECURITY_INFORMATION)) { pISD->Control |= SE_DACL_AUTO_INHERITED; } if(FLAG_ON(SeInfo, SACL_SECURITY_INFORMATION)) { pISD->Control |= SE_SACL_AUTO_INHERITED; } // // Go ahead and upgrade it to autoinherit // if(!FLAG_ON(SeInfo, OWNER_SECURITY_INFORMATION) || !FLAG_ON(SeInfo, GROUP_SECURITY_INFORMATION)) { // // Need to reread it to get the owner and group // AccFree(pVerifySD); dwErr = ReadRegistrySecurityInfo(hReg, SeInfo | OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION, &pVerifySD); // // If we failed to read it because we didn't originally have permissions // and we have the path, we'll try to reopen the handle with the // proper rights // if(dwErr == ERROR_ACCESS_DENIED && pwszPath != NULL) { HKEY hReg2; dwErr = OpenRegistryObject(pwszPath, RegGetDesiredAccess(READ_ACCESS_RIGHTS, SeInfo | OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION), (PHANDLE)&hReg2); if(dwErr == ERROR_SUCCESS) { dwErr = ReadRegistrySecurityInfo(hReg2, SeInfo | OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION, &pVerifySD); RegCloseKey(hReg2); } } // // Set our owner/group in the old security descriptor // if(dwErr == ERROR_SUCCESS) { BOOL fDefaulted; if(!FLAG_ON(SeInfo, OWNER_SECURITY_INFORMATION)) { if(!GetSecurityDescriptorOwner(pVerifySD, &pOwner, &fDefaulted)) { dwErr = GetLastError(); } } if(!FLAG_ON(SeInfo, GROUP_SECURITY_INFORMATION)) { if(!GetSecurityDescriptorGroup(pVerifySD, &pGroup, &fDefaulted)) { dwErr = GetLastError(); } } if(dwErr == ERROR_SUCCESS) { // // If it's self relative, we'll have to make it absolute. // if(FLAG_ON(((SECURITY_DESCRIPTOR *)pSD)->Control, SE_SELF_RELATIVE)) { PSECURITY_DESCRIPTOR pSD2; dwErr = MakeSDAbsolute(pSD, SeInfo, &pSD2, pOwner, pGroup); if(dwErr == ERROR_SUCCESS) { AccFree(pSD); pSD = pSD2; } } else { if(pOwner != NULL) { if(SetSecurityDescriptorOwner(pOldObjSD, pOwner, FALSE) == FALSE) { dwErr = GetLastError(); } } if(pGroup != NULL) { if(SetSecurityDescriptorGroup(pOldObjSD, pGroup, FALSE) == FALSE) { dwErr = GetLastError(); } } } } } } if(dwErr == ERROR_SUCCESS) { dwErr = GetCurrentToken( &hProcessToken ); } if(dwErr == ERROR_SUCCESS) { dwErr = ConvertToAutoInheritSD(pParentSD, pOldObjSD, TRUE, &gRegGenMapping, &pUpdatedSD); if(dwErr == ERROR_SUCCESS) { // // Now, if we're going to do manual propagation, // we'll write out the old SD until we get everyone // else updated // PSECURITY_DESCRIPTOR pWriteSD = pUpdatedSD; if(fManualProp == TRUE) { pWriteSD = pOldObjSD; } else { if(SetPrivateObjectSecurity(SeInfo, pParentSD, &pUpdatedSD, &gRegGenMapping, hProcessToken) == FALSE) { dwErr = GetLastError(); } } // // Reset it... // if(dwErr == ERROR_SUCCESS) { dwErr = SetRegistrySecurityInfo(hReg, SeInfo, NULL, pWriteSD); } } } else { pVerifySD = NULL; } } } } } else { if(dwErr == ERROR_SUCCESS && FLAG_ON(SeInfo, OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION)) { dwErr = SetRegistrySecurityInfo(hReg, SeInfo, NULL, pSD); } } // // Ok, now if we're doing propagation, we'll get busy and do that... // if(dwErr == ERROR_SUCCESS && fManualProp == TRUE) { // // Set our protected flags. If we aren't messing with a particular acl, we'll // pretend it's protected // fProtected = ((SECURITY_DESCRIPTOR *)pUpdatedSD)->Control & ~(SE_DACL_PROTECTED | SE_SACL_PROTECTED); if(FLAG_ON(fProtected, SE_DACL_PROTECTED ) || FLAG_ON(fProtected, SE_SACL_PROTECTED )) { dwErr = InsertPropagationFailureEntry(FailureLogList, 0, fProtected, pwszPath == NULL ? L"" : pwszPath); } if(!FLAG_ON(SeInfo, DACL_SECURITY_INFORMATION)) { fProtected |= SE_DACL_PROTECTED; } if(!FLAG_ON(SeInfo, SACL_SECURITY_INFORMATION)) { fProtected |= SE_SACL_PROTECTED; } // // Ok, go ahead and do deep. This will possibly save us // some storage space in the long run... // dwErr = PropagateRegRightsDeep(pOldObjSD, pUpdatedSD, SeInfo, hReg, pcProcessed, pfStopFlag, fProtected, hProcessToken, FailureLogList); if(dwErr == ERROR_ACCESS_DENIED) { dwErr = InsertPropagationFailureEntry(FailureLogList, dwErr, 0, pwszPath == NULL ? L"" : pwszPath); } // // If that worked, write out our updated root security descriptor // if(dwErr == ERROR_SUCCESS) { PSECURITY_DESCRIPTOR pSet; if(!FLAG_ON(SeInfo, OWNER_SECURITY_INFORMATION)) { if(!SetSecurityDescriptorOwner(pSD, pOwner, FALSE)) { dwErr = GetLastError(); } } if(!FLAG_ON(SeInfo, GROUP_SECURITY_INFORMATION)) { if(!SetSecurityDescriptorGroup(pSD, pGroup, FALSE)) { dwErr = GetLastError(); } } if(dwErr == ERROR_SUCCESS) { if(CreatePrivateObjectSecurityEx(pParentSD, pSD, &pSet, NULL, TRUE, SEF_DACL_AUTO_INHERIT | SEF_SACL_AUTO_INHERIT | SEF_AVOID_OWNER_CHECK | SEF_AVOID_PRIVILEGE_CHECK, hProcessToken, &gRegGenMapping) == FALSE) { dwErr = GetLastError(); } else { dwErr = SetRegistrySecurityInfo(hReg, SeInfo, NULL, pSet); DestroyPrivateObjectSecurity(&pSet); } } } } if(dwErr == ERROR_SUCCESS) { dwErr = WritePropagationFailureList(MARTAEVT_REGISTRY_PROPAGATION_FAILED, FailureLogList, hProcessToken); } RegCleanup: AccFree(pOldObjSD); AccFree(pVerifySD); if(pUpdatedSD != NULL) { DestroyPrivateObjectSecurity(&pUpdatedSD); } acDebugOut((DEB_TRACE, "Out SetAndPropRegRights: %ld\n", dwErr)); return(dwErr); } //+--------------------------------------------------------------------------- // // Function: ConvertRegHandleToName // // Synopsis: Determines the registry path for a handle. Issues an // NtQueryInformationFile to determine the path name // // Arguments: [IN hKey] -- The (open) handle of the file // object // [OUT ppwszName] -- Where the name is returned // // Returns: ERROR_SUCCESS -- Succcess // ERROR_NOT_ENOUGH_MEMORY -- A memory allocation failed // // Notes: The returned memory must be freed with AccFree // //---------------------------------------------------------------------------- DWORD ConvertRegHandleToName(IN HKEY hKey, OUT PWSTR *ppwszName) { DWORD dwErr = ERROR_SUCCESS; // // First, determine the size of the buffer we need... // BYTE pBuff[512]; ULONG cLen = 0; POBJECT_NAME_INFORMATION pNI = NULL; PWSTR pwszPath = NULL; NTSTATUS Status = NtQueryObject(hKey, ObjectNameInformation, (POBJECT_NAME_INFORMATION)pBuff, 512, &cLen); if(!NT_SUCCESS(Status)) { if(Status == STATUS_BUFFER_TOO_SMALL || Status == STATUS_INFO_LENGTH_MISMATCH) { // // Fine.. Allocate a big enough buffer // pNI = (POBJECT_NAME_INFORMATION)AccAlloc(cLen); if(pNI != NULL) { Status = NtQueryObject(hKey, ObjectNameInformation, pNI, cLen, NULL); if(NT_SUCCESS(Status)) { pwszPath = pNI->Name.Buffer; } AccFree(pNI); } else { Status = STATUS_NO_MEMORY; } } if(dwErr == ERROR_SUCCESS) { dwErr = RtlNtStatusToDosError(Status); } } else { pwszPath = ((POBJECT_NAME_INFORMATION)pBuff)->Name.Buffer; } // // If we have a path, then it's a simple matter to pull out the appropriate string, // since what gets returned is something in the form of \\REGISTRY\\MACHINE\\somepath // which is pretty close to what we want // #define REG_OBJ_TAG L"\\REGISTRY\\" if(pwszPath != NULL) { pwszPath += (sizeof(REG_OBJ_TAG) / sizeof(WCHAR) - 1); ACC_ALLOC_AND_COPY_STRINGW(pwszPath, *ppwszName, dwErr); } return(dwErr); }