/*++ Copyright (c) 1997 Microsoft Corporation Module Name: aclmgmt.cpp Abstract: Routines to manage access control lists Geoffrey Guo (geoffguo) 26-Apr-2002 Created Revision History: --*/ #include "clmt.h" #define STRSAFE_LIB #include DWORD ChangeOwner ( LPTSTR lpObjectName, SE_OBJECT_TYPE ObjectType) { DWORD dwRet = ERROR_SUCCESS; PSID psidAdministrators; SID_IDENTIFIER_AUTHORITY siaNtAuthority = SECURITY_NT_AUTHORITY; SECURITY_INFORMATION si = OWNER_SECURITY_INFORMATION; if(!AllocateAndInitializeSid(&siaNtAuthority, 2, SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_ADMINS, 0, 0, 0, 0, 0, 0, &psidAdministrators)) { dwRet = GetLastError(); goto Exit; } EnablePrivilege(SE_TAKE_OWNERSHIP_NAME,TRUE); si |= SI_OWNER_RECURSE; dwRet = SetNamedSecurityInfo(lpObjectName, ObjectType, si, psidAdministrators, NULL, NULL, NULL); EnablePrivilege(SE_TAKE_OWNERSHIP_NAME,FALSE); FreeSid(psidAdministrators); Exit: return dwRet; } //-----------------------------------------------------------------------// // // CopyACL: Copy ACL // // OldACL: Pointer to source Access Control List // NewACL: Pointer to destination Access Control List //-----------------------------------------------------------------------// DWORD CopyACL ( PACL OldACL, PACL NewACL) { ACL_SIZE_INFORMATION aclSizeInfo; LPVOID ace; ACE_HEADER *aceHeader; ULONG i; GetAclInformation (OldACL, (LPVOID) &aclSizeInfo, (DWORD) sizeof (aclSizeInfo), AclSizeInformation); // // Copy all of the ACEs to the new ACL // for (i = 0; i < aclSizeInfo.AceCount; i++) { // // Get the ACE and header info // if (!GetAce (OldACL, i, &ace)) return GetLastError(); aceHeader = (ACE_HEADER *) ace; // // Add the ACE to the new list // if (!AddAce (NewACL, ACL_REVISION, 0xffffffff, ace, aceHeader->AceSize)) return GetLastError(); } return ERROR_SUCCESS; } //-----------------------------------------------------------------------// // // AddAccessAllowedACEToACL: Add Administrator Allowed ACE to ACL // // Acl: Pointer to Access Control List // PermissionMask: Permission will be set for new ACE //-----------------------------------------------------------------------// DWORD AddAccessAllowedACEToACL ( PACL *Acl, DWORD PermissionMask) { ACL_SIZE_INFORMATION aclSizeInfo; int aclSize; DWORD dwRet = ERROR_SUCCESS; USHORT AceSize; PSID psidAdministrators; SID_IDENTIFIER_AUTHORITY siaNtAuthority = SECURITY_NT_AUTHORITY; PACL oldACL, newACL; ACCESS_ALLOWED_ACE *pAllowedAce; oldACL = *Acl; if(!AllocateAndInitializeSid(&siaNtAuthority, 2, SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_ADMINS, 0, 0, 0, 0, 0, 0, &psidAdministrators)) { dwRet = GetLastError(); goto Exit; } GetAclInformation (oldACL, (LPVOID) &aclSizeInfo, (DWORD) sizeof (ACL_SIZE_INFORMATION), AclSizeInformation); aclSize = aclSizeInfo.AclBytesInUse + sizeof (ACL) + sizeof (ACCESS_ALLOWED_ACE) + GetLengthSid (psidAdministrators) - sizeof (DWORD); newACL = (PACL) calloc(aclSize, 1); if (!newACL) { dwRet = ERROR_NOT_ENOUGH_MEMORY; goto Exit1; } if (!InitializeAcl (newACL, aclSize, ACL_REVISION)) { dwRet = GetLastError(); free (newACL); goto Exit1; } dwRet = CopyACL (oldACL, newACL); if (dwRet != ERROR_SUCCESS) { free (newACL); goto Exit1; } AceSize = sizeof(ACE_HEADER) + sizeof(ACCESS_MASK) + (USHORT)GetLengthSid(psidAdministrators); pAllowedAce = (ACCESS_ALLOWED_ACE *)calloc(AceSize, 1); if (!(pAllowedAce)) { dwRet = ERROR_NOT_ENOUGH_MEMORY; goto Exit1; } pAllowedAce->Header.AceFlags = 0; pAllowedAce->Header.AceType = ACCESS_ALLOWED_ACE_TYPE; pAllowedAce->Header.AceSize = AceSize; pAllowedAce->Mask = PermissionMask; CopySid(GetLengthSid(psidAdministrators), &(pAllowedAce->SidStart), psidAdministrators); if (!AddAce (newACL, ACL_REVISION, 0, pAllowedAce, AceSize)) { dwRet = GetLastError(); free (newACL); goto Exit1; } *Acl = newACL; Exit1: FreeSid(psidAdministrators); Exit: return dwRet; } //-----------------------------------------------------------------------// // // RestoreACE: Restore denied ACE // // pACE: Pointer to source Access Control Entry // lpOnjectName: Pointer to the object name //-----------------------------------------------------------------------// DWORD RestoreACE ( PACL *ppAcl, LPTSTR lpObjectName) { DWORD dwRet = ERROR_INVALID_PARAMETER; DWORD dwLen; PACL newACL; LPDENIED_ACE_LIST pList; if (!*ppAcl) goto Exit; pList = g_DeniedACEList; if (pList) { do { if (lstrcmp(lpObjectName, pList->lpObjectName) == 0) { newACL = (PACL) calloc(pList->dwAclSize, 1); if (!newACL) { dwRet = ERROR_NOT_ENOUGH_MEMORY; goto Exit; } if (!InitializeAcl (newACL, pList->dwAclSize, ACL_REVISION)) { free(newACL); dwRet = GetLastError(); goto Exit; } dwRet = CopyACL (*ppAcl, newACL); if (dwRet != ERROR_SUCCESS) { free(newACL); dwRet = GetLastError(); goto Exit; } if (!AddAce (newACL, ACL_REVISION, 0, pList->pace, ((PACE_HEADER)(pList->pace))->AceSize)) { dwRet = GetLastError(); goto Exit; } else DPF(REGmsg, L"Restore denied ACE to ACL: ObjectName=%s", lpObjectName); if (pList->previous) pList->previous->next = pList->next; else g_DeniedACEList = pList->next; if (pList->next) pList->next->previous = pList->previous; *ppAcl = newACL; free (pList->lpObjectName); free (pList->pace); free (pList); break; } pList = pList->next; } while (pList->next); } dwRet = ERROR_SUCCESS; Exit: return dwRet; } //-----------------------------------------------------------------------// // // RemoveACEFromACL: Remove Administrator ACE from ACL // // Acl: Pointer to Access Control List //-----------------------------------------------------------------------// DWORD RemoveACEFromACL ( PACL Acl) { ACL_SIZE_INFORMATION aclSizeInfo; ULONG i; LPVOID ace; ACCESS_ALLOWED_ACE *accessAllowedAce; ACCESS_DENIED_ACE *accessDeniedAce; SYSTEM_AUDIT_ACE *systemAuditAce; PSID psidAdministrators; DWORD returnValue; SID_IDENTIFIER_AUTHORITY siaNtAuthority = SECURITY_NT_AUTHORITY; ACE_HEADER *aceHeader; if(!AllocateAndInitializeSid(&siaNtAuthority, 2, SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_ADMINS, 0, 0, 0, 0, 0, 0, &psidAdministrators)) return GetLastError(); GetAclInformation (Acl, (LPVOID) &aclSizeInfo, (DWORD) sizeof (ACL_SIZE_INFORMATION), AclSizeInformation); for (i = 0; i < aclSizeInfo.AceCount; i++) { if (!GetAce (Acl, i, &ace)) { FreeSid(psidAdministrators); return GetLastError(); } aceHeader = (ACE_HEADER *) ace; if (aceHeader->AceType == ACCESS_ALLOWED_ACE_TYPE) { accessAllowedAce = (ACCESS_ALLOWED_ACE *) ace; if (EqualSid (psidAdministrators, (PSID) &accessAllowedAce->SidStart)) { DeleteAce (Acl, i); FreeSid(psidAdministrators); return ERROR_SUCCESS; } } } FreeSid(psidAdministrators); return ERROR_NO_MORE_ITEMS; } //-----------------------------------------------------------------------// // // AddACE2List: Add ACE to the list // // pACE: Pointer to source Access Control Entry // lpOnjectName: Pointer to the object name //-----------------------------------------------------------------------// DWORD AddACE2List ( ACCESS_DENIED_ACE *pACE, LPTSTR lpObjectName, DWORD dwAclSize) { HRESULT hr; DWORD dwRet = ERROR_SUCCESS; DWORD dwLen; LPDENIED_ACE_LIST pList1; LPDENIED_ACE_LIST pList2; if (!pACE) { dwRet = ERROR_INVALID_PARAMETER; goto Exit; } pList1 = (LPDENIED_ACE_LIST)calloc (sizeof(DENIED_ACE_LIST), 1); if (!pList1) { dwRet = ERROR_NOT_ENOUGH_MEMORY; goto Exit; } pList1->next = NULL; pList1->dwAclSize = dwAclSize; pList1->pace = (ACCESS_DENIED_ACE *)calloc(((PACE_HEADER)pACE)->AceSize, 1); if (!(pList1->pace)) { dwRet = ERROR_NOT_ENOUGH_MEMORY; free (pList1); goto Exit; } memcpy (pList1->pace, pACE, ((PACE_HEADER)pACE)->AceSize); dwLen = lstrlen(lpObjectName)+1; pList1->lpObjectName = (LPTSTR)calloc (dwLen, sizeof(TCHAR)); if (!(pList1->lpObjectName)) { dwRet = ERROR_NOT_ENOUGH_MEMORY; free (pList1); free (pList1->pace); goto Exit; } hr = StringCchCopy (pList1->lpObjectName, dwLen, lpObjectName); if (hr != S_OK) { dwRet = HRESULT_CODE(hr); free (pList1); free (pList1->pace); goto Exit; } pList2 = g_DeniedACEList; if (pList2) { while (pList2->next) pList2 = pList2->next; pList2->next = pList1; pList1->previous = pList2; } else { g_DeniedACEList = pList1; pList1->previous = NULL; } Exit: return dwRet; } //-----------------------------------------------------------------------// // // RemoveDeniedACEFromACL: Remove Denied ACE from ACL // // Acl: Pointer to Access Control List // lpOnjectName: Pointer to the object name //-----------------------------------------------------------------------// DWORD RemoveDeniedACEFromACL ( PACL Acl, LPTSTR lpObjectName) { ACL_SIZE_INFORMATION aclSizeInfo; ULONG i; ACCESS_DENIED_ACE *accessDeniedAce; LPVOID ace; DWORD dwRet = ERROR_SUCCESS; ACE_HEADER *aceHeader; GetAclInformation (Acl, (LPVOID) &aclSizeInfo, (DWORD) sizeof (ACL_SIZE_INFORMATION), AclSizeInformation); for (i = 0; i < aclSizeInfo.AceCount; i++) { if (!GetAce (Acl, i, &ace)) { dwRet = GetLastError(); if (dwRet == ERROR_NO_MORE_ITEMS) dwRet = ERROR_SUCCESS; } aceHeader = (ACE_HEADER *) ace; if (aceHeader->AceType == ACCESS_DENIED_ACE_TYPE) { accessDeniedAce = (ACCESS_DENIED_ACE *) ace; AddACE2List (accessDeniedAce, lpObjectName, aclSizeInfo.AclBytesInUse); if (!DeleteAce (Acl, i)) dwRet = GetLastError(); else DPF(REGmsg, L"Remove denied ACE from ACL: ObjectName=%s", lpObjectName); break; } } return dwRet; } //-----------------------------------------------------------------------// // // AdjustObjectSecurity: Add full control ACE for local administrator. // // lpObjectName: Object Name // ObjectType: Object Type // ppOldSidOwner: Current object owner // bSetOrRestore: True --- Add ACE, FALSE --- Remove ACE //-----------------------------------------------------------------------// DWORD AdjustObjectSecurity ( LPTSTR lpObjectName, SE_OBJECT_TYPE ObjectType, BOOL bSetOrRestore) { DWORD dwErr; HRESULT hr; PSID psid = NULL; PSECURITY_DESCRIPTOR pSD; PACL pDacl = NULL; PACL psidDacl = NULL; SECURITY_INFORMATION secInfo = DACL_SECURITY_INFORMATION; dwErr = GetNamedSecurityInfo(lpObjectName, ObjectType, secInfo, NULL, NULL, &psidDacl, NULL, &pSD); if (dwErr == ERROR_ACCESS_DENIED) { TCHAR szString[MAX_PATH*2]; TCHAR szCaption[MAX_PATH]; LoadString((HINSTANCE)g_hInstDll, IDS_OWNERSHIP, szCaption, MAX_PATH-1); hr = StringCchPrintf(szString, MAX_PATH*2-1, szCaption, lpObjectName); LoadString((HINSTANCE)g_hInstDll, IDS_MAIN_TITLE, szCaption, MAX_PATH-1); if (SUCCEEDED(hr) && MessageBox(GetConsoleWindow(), szString, szCaption, MB_YESNO) == IDYES) { if (ChangeOwner(lpObjectName, ObjectType) == ERROR_SUCCESS) { DPF(REGmsg, L"Administrator takes over the ownership: ObjectName=%s", lpObjectName); dwErr = GetNamedSecurityInfo(lpObjectName, ObjectType, secInfo, NULL, NULL, &psidDacl, NULL, &pSD); } } } if (dwErr == ERROR_SUCCESS) { pDacl = psidDacl; if (bSetOrRestore) { dwErr = AddAccessAllowedACEToACL (&pDacl, GENERIC_ALL | SPECIFIC_RIGHTS_ALL | STANDARD_RIGHTS_ALL); if (dwErr == ERROR_SUCCESS) DPF(REGmsg, L"Add Administrator with full control to ACL: ObjectName=%s", lpObjectName); else DPF(REGerr, L"Fails to add Administrator with full control to ACL: ObjectName=%s", lpObjectName); } else { dwErr = RemoveACEFromACL(pDacl); if (dwErr == ERROR_SUCCESS) DPF(REGmsg, L"Remove Administrator with full control from ACL: ObjectName=%s", lpObjectName); else DPF(REGerr, L"Fails to remove Administrator with full control from ACL: ObjectName=%s", lpObjectName); } if (dwErr == ERROR_SUCCESS) { dwErr = SetNamedSecurityInfo(lpObjectName, ObjectType, secInfo, NULL, NULL, pDacl, NULL); } if (psidDacl != pDacl && pDacl) free(pDacl); LocalFree(pSD); } return dwErr; } #define ACCESS_STATUS_ALLOWED 0 #define ACCESS_STATUS_DENIED 1 #define ACCESS_STATUS_NOTPRESENT 2 HRESULT GetObjectAccessStatus( LPTSTR lpObjectName, SE_OBJECT_TYPE ObjectType, PSID pOwnerSid, PDWORD pdwStatus) { DWORD dwErr; HRESULT hr; PSECURITY_DESCRIPTOR pSD = NULL; PACL psidDacl = NULL; SECURITY_INFORMATION secInfo = DACL_SECURITY_INFORMATION; ACL_SIZE_INFORMATION aclSizeInfo; LPVOID ace; ACE_HEADER *aceHeader; ULONG i; ACCESS_MASK dwAllowMask,dwDeniedMask; if (!lpObjectName || !pOwnerSid || !pdwStatus) { hr = E_INVALIDARG; goto cleanup; } dwErr = GetNamedSecurityInfo(lpObjectName, ObjectType, secInfo, NULL, NULL, &psidDacl, NULL, &pSD); if (dwErr != ERROR_SUCCESS) { hr = HRESULT_FROM_WIN32(dwErr); goto cleanup; } if (!psidDacl) { // in FS, this must be FAT/FAT32 hr = S_OK; *pdwStatus = ACCESS_STATUS_ALLOWED; goto cleanup; } if (!GetAclInformation (psidDacl, (LPVOID) &aclSizeInfo, (DWORD) sizeof (ACL_SIZE_INFORMATION), AclSizeInformation)) { hr = HRESULT_FROM_WIN32(GetLastError()); goto cleanup; } *pdwStatus = ACCESS_STATUS_NOTPRESENT; dwAllowMask = dwDeniedMask = 0; for (i = 0; i < aclSizeInfo.AceCount; i++) { ACCESS_ALLOWED_ACE *accessAllowedAce; ACCESS_DENIED_ACE *accessDeniedAce; PSID pAclSid; if (!GetAce (psidDacl, i, &ace)) { hr = HRESULT_FROM_WIN32(GetLastError()); goto cleanup; } aceHeader = (ACE_HEADER *) ace; switch (aceHeader->AceType) { case ACCESS_ALLOWED_ACE_TYPE: accessAllowedAce = (ACCESS_ALLOWED_ACE *) ace; pAclSid = (PSID) &accessAllowedAce->SidStart; if (EqualSid (pOwnerSid, pAclSid)) { dwAllowMask |= accessAllowedAce->Mask; } break; case ACCESS_DENIED_ACE_TYPE: accessDeniedAce = (ACCESS_DENIED_ACE *) ace; pAclSid = (PSID) &accessDeniedAce->SidStart; if (EqualSid (pOwnerSid, pAclSid)) { dwDeniedMask |= accessDeniedAce->Mask; } break; default: continue; } } GENERIC_MAPPING gm; gm.GenericRead = FILE_GENERIC_READ ; gm.GenericWrite = FILE_GENERIC_WRITE ; gm.GenericExecute = FILE_GENERIC_EXECUTE ; gm.GenericAll = FILE_ALL_ACCESS ; DWORD dwExpected = GENERIC_ALL|GENERIC_WRITE|GENERIC_READ; MapGenericMask(&dwExpected,&gm); if (AreAnyAccessesGranted(dwDeniedMask,dwExpected)) { *pdwStatus = ACCESS_STATUS_DENIED; hr = S_OK; goto cleanup; } dwExpected = GENERIC_ALL; MapGenericMask(&dwExpected,&gm); if (AreAllAccessesGranted(dwAllowMask,dwExpected)) { *pdwStatus = ACCESS_STATUS_ALLOWED; hr = S_OK; goto cleanup; } dwExpected = GENERIC_WRITE|GENERIC_READ; MapGenericMask(&dwExpected,&gm); if (AreAllAccessesGranted(dwAllowMask,dwExpected)) { *pdwStatus = ACCESS_STATUS_ALLOWED; hr = S_OK; goto cleanup; } *pdwStatus = ACCESS_STATUS_NOTPRESENT; hr = S_OK; cleanup: if (pSD) { LocalFree(pSD); } return hr; } HRESULT IsObjectAccessiablebyLocalSys( LPTSTR lpObjectName, SE_OBJECT_TYPE ObjectType, PBOOL pbCanAccess) { HRESULT hr = S_OK; PSID pUserSid = NULL; DWORD dwLocal,dwEveryone,dwAdmins; SID_IDENTIFIER_AUTHORITY siaNtAuthority = SECURITY_NT_AUTHORITY; *pbCanAccess = FALSE; //Local System if (!ConvertStringSidToSid(TEXT("S-1-5-18"),&pUserSid)) { hr = HRESULT_FROM_WIN32(GetLastError()); pUserSid = NULL; goto cleanup; } hr = GetObjectAccessStatus(lpObjectName,ObjectType,pUserSid,&dwLocal); if (hr != S_OK) { goto cleanup; } if (dwLocal == ACCESS_STATUS_DENIED) { *pbCanAccess = FALSE; goto cleanup; } LocalFree(pUserSid); pUserSid = NULL; //Everyone if (!ConvertStringSidToSid(TEXT("S-1-1-0"),&pUserSid)) { hr = HRESULT_FROM_WIN32(GetLastError()); pUserSid = NULL; goto cleanup; } hr = GetObjectAccessStatus(lpObjectName,ObjectType,pUserSid,&dwEveryone); if (hr != S_OK) { goto cleanup; } if (dwEveryone == ACCESS_STATUS_DENIED) { *pbCanAccess = FALSE; goto cleanup; } LocalFree(pUserSid); pUserSid = NULL; //Local Admins if(!AllocateAndInitializeSid(&siaNtAuthority, 2, SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_ADMINS, 0, 0, 0, 0, 0, 0, &pUserSid)) { hr = HRESULT_FROM_WIN32(GetLastError()); pUserSid = NULL; goto cleanup; } hr = GetObjectAccessStatus(lpObjectName,ObjectType,pUserSid,&dwAdmins); if (hr != S_OK) { goto cleanup; } if (dwAdmins == ACCESS_STATUS_DENIED) { *pbCanAccess = FALSE; goto cleanup; } LocalFree(pUserSid); pUserSid = NULL; if (dwEveryone == ACCESS_STATUS_ALLOWED) { *pbCanAccess = TRUE; } else if ( (dwAdmins ==ACCESS_STATUS_ALLOWED) && (dwLocal ==ACCESS_STATUS_ALLOWED) ) { *pbCanAccess = TRUE; } else { *pbCanAccess = FALSE; } hr = S_OK; cleanup: if (pUserSid) { LocalFree(pUserSid); } return hr; }