windows-nt/Source/XPSP1/NT/shell/osshell/security/rshx32/ntfssi.cpp
2020-09-26 16:20:57 +08:00

1554 lines
48 KiB
C++

//+-------------------------------------------------------------------------
//
// Microsoft Windows
//
// Copyright (C) Microsoft Corporation, 1996 - 1999
//
// File: ntfssi.cpp
//
// This file contains the implementation of the CNTFSSecurity object.
//
//--------------------------------------------------------------------------
#include "rshx32.h"
#include <windowsx.h> // GET_WM_COMMAND_ID, etc.
#include <atlconv.h>
#define MY_FILE_ALL_ACCESS (STANDARD_RIGHTS_REQUIRED \
| SYNCHRONIZE \
| FILE_READ_DATA | FILE_LIST_DIRECTORY \
| FILE_WRITE_DATA | FILE_ADD_FILE \
| FILE_APPEND_DATA | FILE_ADD_SUBDIRECTORY \
| FILE_CREATE_PIPE_INSTANCE \
| FILE_READ_EA \
| FILE_WRITE_EA \
| FILE_EXECUTE | FILE_TRAVERSE \
| FILE_DELETE_CHILD \
| FILE_READ_ATTRIBUTES \
| FILE_WRITE_ATTRIBUTES)
#if(FILE_ALL_ACCESS != MY_FILE_ALL_ACCESS)
#error ACL editor needs to sync with file permissions changes in ntioapi.h (or ntioapi.h is broken)
#endif
#define INHERIT_FULL (CONTAINER_INHERIT_ACE | OBJECT_INHERIT_ACE)
//
// Treat SYNCHRONIZE specially. In particular, always allow SYNCHRONIZE and
// never Deny SYNCHRONIZE. Do this by removing it from the Generic Mapping,
// turning it off in all ACEs and SI_ACCESS entries, and then adding it to
// all Allow ACEs before saving a new ACL.
//
#define FILE_GENERIC_READ_ (FILE_GENERIC_READ & ~SYNCHRONIZE)
#define FILE_GENERIC_WRITE_ (FILE_GENERIC_WRITE & ~(SYNCHRONIZE | READ_CONTROL))
#define FILE_GENERIC_EXECUTE_ (FILE_GENERIC_EXECUTE & ~SYNCHRONIZE)
#define FILE_GENERIC_ALL_ (FILE_ALL_ACCESS & ~SYNCHRONIZE)
#define FILE_GENERAL_MODIFY (FILE_GENERIC_READ_ | FILE_GENERIC_WRITE_ | FILE_GENERIC_EXECUTE_ | DELETE)
#define FILE_GENERAL_PUBLISH (FILE_GENERIC_READ_ | FILE_GENERIC_WRITE_ | FILE_GENERIC_EXECUTE_)
#define FILE_GENERAL_DEPOSIT (FILE_GENERIC_WRITE_ | FILE_GENERIC_EXECUTE_)
#define FILE_GENERAL_READ_EX (FILE_GENERIC_READ_ | FILE_GENERIC_EXECUTE_)
// The following array defines the permission names for NTFS objects.
SI_ACCESS siNTFSAccesses[] =
{
{ &GUID_NULL, FILE_GENERIC_ALL_, MAKEINTRESOURCE(IDS_NTFS_GENERIC_ALL), SI_ACCESS_GENERAL | INHERIT_FULL|SI_ACCESS_SPECIFIC },
{ &GUID_NULL, FILE_GENERAL_MODIFY, MAKEINTRESOURCE(IDS_NTFS_GENERAL_MODIFY), SI_ACCESS_GENERAL | INHERIT_FULL },
{ &GUID_NULL, FILE_GENERAL_READ_EX, MAKEINTRESOURCE(IDS_NTFS_GENERAL_READ), SI_ACCESS_GENERAL | INHERIT_FULL },
{ &GUID_NULL, FILE_GENERAL_READ_EX, MAKEINTRESOURCE(IDS_NTFS_GENERAL_LIST), SI_ACCESS_CONTAINER | CONTAINER_INHERIT_ACE },
{ &GUID_NULL, FILE_GENERIC_READ_, MAKEINTRESOURCE(IDS_NTFS_GENERIC_READ), SI_ACCESS_GENERAL | INHERIT_FULL },
{ &GUID_NULL, FILE_GENERIC_WRITE_, MAKEINTRESOURCE(IDS_NTFS_GENERIC_WRITE), SI_ACCESS_GENERAL | INHERIT_FULL },
{ &GUID_NULL, FILE_EXECUTE, MAKEINTRESOURCE(IDS_NTFS_FILE_EXECUTE), SI_ACCESS_SPECIFIC },
{ &GUID_NULL, FILE_READ_DATA, MAKEINTRESOURCE(IDS_NTFS_FILE_READ_DATA), SI_ACCESS_SPECIFIC },
{ &GUID_NULL, FILE_READ_ATTRIBUTES, MAKEINTRESOURCE(IDS_NTFS_FILE_READ_ATTR), SI_ACCESS_SPECIFIC },
{ &GUID_NULL, FILE_READ_EA, MAKEINTRESOURCE(IDS_NTFS_FILE_READ_EA), SI_ACCESS_SPECIFIC },
{ &GUID_NULL, FILE_WRITE_DATA, MAKEINTRESOURCE(IDS_NTFS_FILE_WRITE_DATA), SI_ACCESS_SPECIFIC },
{ &GUID_NULL, FILE_APPEND_DATA, MAKEINTRESOURCE(IDS_NTFS_FILE_APPEND_DATA), SI_ACCESS_SPECIFIC },
{ &GUID_NULL, FILE_WRITE_ATTRIBUTES,MAKEINTRESOURCE(IDS_NTFS_FILE_WRITE_ATTR), SI_ACCESS_SPECIFIC },
{ &GUID_NULL, FILE_WRITE_EA, MAKEINTRESOURCE(IDS_NTFS_FILE_WRITE_EA), SI_ACCESS_SPECIFIC },
{ &GUID_NULL, FILE_DELETE_CHILD, MAKEINTRESOURCE(IDS_NTFS_FILE_DELETE_CHILD),SI_ACCESS_SPECIFIC },
#if(FILE_CREATE_PIPE_INSTANCE != FILE_APPEND_DATA)
{ &GUID_NULL, FILE_CREATE_PIPE_INSTANCE, MAKEINTRESOURCE(IDS_NTFS_FILE_CREATE_PIPE), SI_ACCESS_SPECIFIC },
#endif
{ &GUID_NULL, DELETE, MAKEINTRESOURCE(IDS_NTFS_STD_DELETE), SI_ACCESS_SPECIFIC },
{ &GUID_NULL, READ_CONTROL, MAKEINTRESOURCE(IDS_NTFS_STD_READ_CONTROL), SI_ACCESS_SPECIFIC },
{ &GUID_NULL, WRITE_DAC, MAKEINTRESOURCE(IDS_NTFS_STD_WRITE_DAC), SI_ACCESS_SPECIFIC },
{ &GUID_NULL, WRITE_OWNER, MAKEINTRESOURCE(IDS_NTFS_STD_WRITE_OWNER), SI_ACCESS_SPECIFIC },
// { &GUID_NULL, SYNCHRONIZE, MAKEINTRESOURCE(IDS_NTFS_STD_SYNCHRONIZE), SI_ACCESS_SPECIFIC },
{ &GUID_NULL, 0, MAKEINTRESOURCE(IDS_NONE), 0 },
{ &GUID_NULL, FILE_GENERIC_EXECUTE_,MAKEINTRESOURCE(IDS_NTFS_GENERIC_EXECUTE), 0 },
{ &GUID_NULL, FILE_GENERAL_DEPOSIT, MAKEINTRESOURCE(IDS_NTFS_GENERAL_DEPOSIT), 0 },
{ &GUID_NULL, FILE_GENERAL_PUBLISH, MAKEINTRESOURCE(IDS_NTFS_GENERAL_PUBLISH), 0 },
};
#define iNTFSDefAccess 2 // FILE_GENERAL_READ_EX
#define iNTFSDelChildAccess 14 // FILE_DELETE_CHILD
// The following array defines the inheritance types for NTFS directories.
SI_INHERIT_TYPE siNTFSInheritTypes[] =
{
&GUID_NULL, 0, MAKEINTRESOURCE(IDS_NTFS_FOLDER),
&GUID_NULL, CONTAINER_INHERIT_ACE | OBJECT_INHERIT_ACE, MAKEINTRESOURCE(IDS_NTFS_FOLDER_SUBITEMS),
&GUID_NULL, CONTAINER_INHERIT_ACE, MAKEINTRESOURCE(IDS_NTFS_FOLDER_SUBFOLDER),
&GUID_NULL, OBJECT_INHERIT_ACE, MAKEINTRESOURCE(IDS_NTFS_FOLDER_FILE),
&GUID_NULL, INHERIT_ONLY_ACE | CONTAINER_INHERIT_ACE | OBJECT_INHERIT_ACE, MAKEINTRESOURCE(IDS_NTFS_SUBITEMS_ONLY),
&GUID_NULL, INHERIT_ONLY_ACE | CONTAINER_INHERIT_ACE, MAKEINTRESOURCE(IDS_NTFS_SUBFOLDER_ONLY),
&GUID_NULL, INHERIT_ONLY_ACE | OBJECT_INHERIT_ACE, MAKEINTRESOURCE(IDS_NTFS_FILE_ONLY),
};
VOID ProgressFunction(IN LPWSTR pObjectName,
IN DWORD Status,
IN OUT PPROG_INVOKE_SETTING pInvokeSetting ,
IN PVOID Args,
IN BOOL SecuritySet);
BOOL SetFileSecurityUsingNTName(IN PUNICODE_STRING pFileName,
IN PSECURITY_DESCRIPTOR pSecurityDescriptor,
OUT PBOOL pbIsFile);
STDMETHODIMP
CheckFileAccess(LPCTSTR pszObjectName, LPDWORD pdwAccessGranted)
{
HRESULT hr = S_OK;
UINT i;
UNICODE_STRING usNtFileName = {0};
DWORD dwAccessDesired[] = { ALL_SECURITY_ACCESS,
READ_CONTROL,
WRITE_DAC,
WRITE_OWNER,
ACCESS_SYSTEM_SECURITY };
TraceEnter(TRACE_NTFSSI, "CheckFileAccess");
TraceAssert(pdwAccessGranted != NULL);
*pdwAccessGranted = 0;
if (!RtlDosPathNameToNtPathName_U(pszObjectName,
&usNtFileName,
NULL,
NULL))
{
ExitGracefully(hr, E_OUTOFMEMORY, "RtlDosPathNameToNtPathName_U failed");
}
for (i = 0; i < ARRAYSIZE(dwAccessDesired); i++)
{
OBJECT_ATTRIBUTES oa;
IO_STATUS_BLOCK StatusBlock;
DWORD dwErr;
HANDLE hFile;
if ((dwAccessDesired[i] & *pdwAccessGranted) == dwAccessDesired[i])
continue; // already have this access
InitializeObjectAttributes(&oa,
&usNtFileName,
OBJ_CASE_INSENSITIVE,
0,
0);
dwErr = RtlNtStatusToDosError(NtOpenFile(&hFile,
dwAccessDesired[i],
&oa,
&StatusBlock,
FILE_SHARE_READ | FILE_SHARE_WRITE,
0));
if (dwErr == 0)
{
*pdwAccessGranted |= dwAccessDesired[i];
NtClose(hFile);
}
}
exit_gracefully:
RtlFreeUnicodeString(&usNtFileName);
Trace((TEXT("Access = 0x%08x"), *pdwAccessGranted));
TraceLeaveResult(hr);
}
DWORD
GetCurrentToken(DWORD dwDesiredAccess, PHANDLE phToken)
{
DWORD dwErr = NOERROR;
if (!OpenThreadToken(GetCurrentThread(), dwDesiredAccess, TRUE, phToken)
&& !OpenProcessToken(GetCurrentProcess(), dwDesiredAccess, phToken))
{
dwErr = GetLastError();
}
return dwErr;
}
///////////////////////////////////////////////////////////
//
// Constructor/destructor
//
///////////////////////////////////////////////////////////
NTFS_COMPARE_DATA::~NTFS_COMPARE_DATA()
{
LocalFreeString(&pszSaclConflict);
LocalFreeString(&pszDaclConflict);
}
CNTFSSecurity::CNTFSSecurity(SE_OBJECT_TYPE seType)
: CSecurityInformation(seType)
{
}
CNTFSSecurity::~CNTFSSecurity()
{
if (m_pCompareData != NULL)
m_pCompareData->bAbortThread = TRUE;
WaitForComparison();
delete m_pCompareData;
}
STDMETHODIMP
CNTFSSecurity::Initialize(HDPA hItemList,
DWORD dwFlags,
LPTSTR pszServer,
LPTSTR pszObject)
{
HRESULT hr;
//
// If we're editing the owner on a folder, turn on the Recurse button.
//
if (dwFlags & SI_CONTAINER)
{
if ((dwFlags & (SI_EDIT_OWNER | SI_OWNER_READONLY)) == SI_EDIT_OWNER)
dwFlags |= SI_OWNER_RECURSE;
if (!(dwFlags & SI_READONLY))
dwFlags |= SI_RESET_DACL_TREE;
if (dwFlags & SI_EDIT_AUDITS)
dwFlags |= SI_RESET_SACL_TREE;
}
//
// Let the base class do its thing
//
hr = CSecurityInformation::Initialize(hItemList,
dwFlags,
pszServer,
pszObject);
//
// If multiple selection, create thread to compare security descriptors
//
if (m_hItemList && DPA_GetPtrCount(m_hItemList) > 1)
{
m_pCompareData = new NTFS_COMPARE_DATA(m_hItemList, m_dwSIFlags);
if (m_pCompareData != NULL)
{
DWORD dwID;
m_hCompareThread = CreateThread(NULL,
0,
NTFSCompareThreadProc,
m_pCompareData,
CREATE_SUSPENDED,
&dwID);
if (m_hCompareThread != NULL)
{
SetThreadPriority(m_hCompareThread, THREAD_PRIORITY_BELOW_NORMAL);
ResumeThread(m_hCompareThread);
}
else
{
delete m_pCompareData;
m_pCompareData = NULL;
}
}
}
return hr;
}
///////////////////////////////////////////////////////////
//
// ISecurityInformation methods
//
///////////////////////////////////////////////////////////
STDMETHODIMP
CNTFSSecurity::GetAccessRights(const GUID* /*pguidObjectType*/,
DWORD dwFlags,
PSI_ACCESS *ppAccesses,
ULONG *pcAccesses,
ULONG *piDefaultAccess)
{
TraceEnter(TRACE_NTFSSI, "CNTFSSecurity::GetAccessRights");
TraceAssert(ppAccesses != NULL);
TraceAssert(pcAccesses != NULL);
TraceAssert(piDefaultAccess != NULL);
//
//Don't Show delete subfolder and files for files or
//when applyonto is files only
//
if(IsFile())
siNTFSAccesses[iNTFSDelChildAccess].dwFlags = 0;
else
siNTFSAccesses[iNTFSDelChildAccess].dwFlags = SI_ACCESS_SPECIFIC;
*ppAccesses = siNTFSAccesses;
*pcAccesses = ARRAYSIZE(siNTFSAccesses);
*piDefaultAccess = iNTFSDefAccess;
TraceLeaveResult(S_OK);
}
GENERIC_MAPPING NTFSMap =
{
FILE_GENERIC_READ_,
FILE_GENERIC_WRITE_,
FILE_GENERIC_EXECUTE_,
FILE_GENERIC_ALL_
};
STDMETHODIMP
CNTFSSecurity::MapGeneric(const GUID* /*pguidObjectType*/,
UCHAR * /*pAceFlags*/,
ACCESS_MASK *pmask)
{
TraceEnter(TRACE_NTFSSI, "CNTFSSecurity::MapGeneric");
TraceAssert(pmask != NULL);
MapGenericMask(pmask, &NTFSMap);
*pmask &= ~SYNCHRONIZE;
TraceLeaveResult(S_OK);
}
STDMETHODIMP
CNTFSSecurity::GetInheritTypes(PSI_INHERIT_TYPE *ppInheritTypes,
ULONG *pcInheritTypes)
{
TraceEnter(TRACE_NTFSSI, "CNTFSSecurity::GetInheritTypes");
TraceAssert(ppInheritTypes != NULL);
TraceAssert(pcInheritTypes != NULL);
if (m_dwSIFlags & SI_CONTAINER)
{
*ppInheritTypes = siNTFSInheritTypes;
*pcInheritTypes = ARRAYSIZE(siNTFSInheritTypes);
TraceLeaveResult(S_OK);
}
TraceLeaveResult(E_NOTIMPL);
}
STDMETHODIMP
CNTFSSecurity::GetSecurity(SECURITY_INFORMATION si,
PSECURITY_DESCRIPTOR *ppSD,
BOOL fDefault)
{
HRESULT hr = S_OK;
SECURITY_INFORMATION siConflict = 0;
TraceEnter(TRACE_NTFSSI, "CNTFSSecurity::GetSecurity");
TraceAssert(si != 0);
TraceAssert(ppSD != NULL);
*ppSD = NULL;
if (fDefault)
{
#ifdef SUPPORT_NTFS_DEFAULT
hr = GetDefaultSecurity(si, ppSD);
ExitGracefully(hr, hr, "Returning default security descriptor");
#else
ExitGracefully(hr, E_NOTIMPL, "Default security descriptor not supported");
#endif
}
WaitForComparison();
if (m_pCompareData != NULL)
{
// Of the bits requested (si) figure out which ones
// conflict and which ones don't.
// First check DACL and SACL
SECURITY_INFORMATION siAcl = si & m_pCompareData->siConflict;
siAcl &= (SACL_SECURITY_INFORMATION | DACL_SECURITY_INFORMATION);
if (siAcl)
{
// To get here, we've already told the user there is a conflict
// and they said to reset the ACL(s) and continue.
// Build security descriptor with empty DACL and NULL SACL
SECURITY_DESCRIPTOR sdEmpty = {0};
ACL aclEmpty = {0};
InitializeSecurityDescriptor(&sdEmpty, SECURITY_DESCRIPTOR_REVISION);
InitializeAcl(&aclEmpty, sizeof(ACL), ACL_REVISION);
SetSecurityDescriptorSacl(&sdEmpty, TRUE, &aclEmpty, FALSE);
SetSecurityDescriptorDacl(&sdEmpty, TRUE, &aclEmpty, FALSE);
// Reset the DACL and/or SACL
hr = SetSecurity(siAcl, (PSECURITY_DESCRIPTOR)&sdEmpty);
}
// Note that SetSecurity will free m_pCompareData if there are
// no more conflicts, so check the pointer again here.
if (m_pCompareData != NULL)
siConflict = si & m_pCompareData->siConflict;
}
// Read it from the first item.
hr = CSecurityInformation::GetSecurity(si, ppSD, fDefault);
if (SUCCEEDED(hr) && siConflict != 0)
{
//
// Clear out any of the parts that conflict
//
PISECURITY_DESCRIPTOR psd = (PISECURITY_DESCRIPTOR)*ppSD;
TraceAssert(psd != NULL);
if (siConflict & OWNER_SECURITY_INFORMATION)
{
psd->Owner = NULL;
}
if (siConflict & GROUP_SECURITY_INFORMATION)
{
psd->Group = NULL;
}
// The following can happen if there was an error resetting ACLs above
if (siConflict & SACL_SECURITY_INFORMATION)
{
psd->Control &= ~SE_SACL_PRESENT;
psd->Sacl = NULL;
}
if (siConflict & DACL_SECURITY_INFORMATION)
{
psd->Control &= ~SE_DACL_PRESENT;
psd->Dacl = NULL;
}
}
exit_gracefully:
TraceLeaveResult(hr);
}
//
// See comments about SYNCHRONIZE at the top of this file
//
void
FixSynchronizeAccess(SECURITY_INFORMATION si, PSECURITY_DESCRIPTOR pSD)
{
if (NULL != pSD && 0 != (si & DACL_SECURITY_INFORMATION))
{
BOOL bPresent;
BOOL bDefault;
PACL pDacl = NULL;
GetSecurityDescriptorDacl(pSD, &bPresent, &pDacl, &bDefault);
if (pDacl)
{
PACE_HEADER pAce;
int i;
for (i = 0, pAce = (PACE_HEADER)FirstAce(pDacl);
i < pDacl->AceCount;
i++, pAce = (PACE_HEADER)NextAce(pAce))
{
if (ACCESS_ALLOWED_ACE_TYPE == pAce->AceType)
((PKNOWN_ACE)pAce)->Mask |= SYNCHRONIZE;
}
}
}
}
STDMETHODIMP
CNTFSSecurity::SetSecurity(SECURITY_INFORMATION si,
PSECURITY_DESCRIPTOR pSD)
{
HRESULT hr = S_OK;
if (si & DACL_SECURITY_INFORMATION)
FixSynchronizeAccess(si, pSD);
if(!SetAclOnRemoteNetworkDrive(m_hItemList,
si,
pSD,
GetLastActivePopup(m_hwndOwner)))
{
return S_FALSE;
}
//
// If we need to recursively set the Owner, get the Owner &
// Group from pSD.
//
if (si & ( SI_OWNER_RECURSE | SI_RESET_DACL_TREE | SI_RESET_SACL_TREE ) )
{
si = si & (~( SI_OWNER_RECURSE | SI_RESET_DACL_TREE | SI_RESET_SACL_TREE ));
hr = SetSecurityLocal(si, pSD, NULL);
// Remember whether the user cancelled, since hr gets
// reset when we call the base class below.
}
else
{
// See comments about SYNCHRONIZE at the top of this file
// Call the base class to do the rest
hr = CSecurityInformation::SetSecurity(si, pSD);
}
if (S_OK == hr && m_pCompareData)
{
// If we successfully wrote it, then it doesn't conflict anymore
m_pCompareData->siConflict &= ~(si);
if (0 == m_pCompareData->siConflict)
{
delete m_pCompareData;
m_pCompareData = NULL;
}
}
return hr;
}
STDMETHODIMP
CNTFSSecurity::PropertySheetPageCallback(HWND hwnd,
UINT uMsg,
SI_PAGE_TYPE uPage)
{
HRESULT hr;
LPUINT pidsPrompt = NULL;
LPCTSTR pszFile2 = NULL;
TraceEnter(TRACE_NTFSSI, "CNTFSSecurity::PropertySheetPageCallback");
hr = CSecurityInformation::PropertySheetPageCallback(hwnd, uMsg, uPage);
if (uMsg == PSPCB_SI_INITDIALOG)
{
WaitForComparison();
if (m_pCompareData != NULL)
{
if (SUCCEEDED(m_pCompareData->hrResult))
{
switch (uPage)
{
case SI_PAGE_PERM:
case SI_PAGE_ADVPERM:
pidsPrompt = &m_pCompareData->idsDaclPrompt;
pszFile2 = m_pCompareData->pszDaclConflict;
break;
case SI_PAGE_AUDIT:
pidsPrompt = &m_pCompareData->idsSaclPrompt;
pszFile2 = m_pCompareData->pszSaclConflict;
break;
}
}
}
if (pidsPrompt != NULL && *pidsPrompt != 0)
{
if (IDYES != MsgPopup(hwnd,
MAKEINTRESOURCE(*pidsPrompt),
MAKEINTRESOURCE(IDS_PROPPAGE_TITLE),
MB_YESNO | MB_ICONWARNING | MB_SETFOREGROUND,
g_hInstance,
m_pszObjectName,
pszFile2))
{
hr = E_FAIL; // abort
}
// Don't want to prompt again for the same thing, so set
// this to zero.
*pidsPrompt = 0;
}
}
TraceLeaveResult(hr);
}
#ifdef SUPPORT_NTFS_DEFAULT
HRESULT
CNTFSSecurity::GetParentSecurity(SECURITY_INFORMATION si,
PSECURITY_DESCRIPTOR *ppSD)
{
HRESULT hr = S_OK;
LPTSTR pszItem;
TraceEnter(TRACE_NTFSSI, "CNTFSSecurity::GetParentSecurity");
TraceAssert(m_hItemList && DPA_GetPtrCount(m_hItemList));
// Get the name of the first item
pszItem = (LPTSTR)DPA_GetPtr(m_hItemList, 0);
if (NULL != pszItem && !PathIsRoot(pszItem))
{
TCHAR szParent[MAX_PATH];
lstrcpyn(szParent, pszItem, ARRAYSIZE(szParent));
PathRemoveFileSpec(szParent);
// Read the parent DACL/SACL
hr = ReadObjectSecurity(szParent, si, ppSD);
}
TraceLeaveResult(hr);
}
STDMETHODIMP
CNTFSSecurity::GetDefaultSecurity(SECURITY_INFORMATION si,
PSECURITY_DESCRIPTOR *ppSD)
{
HRESULT hr = S_OK;
DWORD dwErr;
PSECURITY_DESCRIPTOR psdParent = NULL;
SECURITY_INFORMATION siLocal;
HANDLE hToken = INVALID_HANDLE_VALUE;
ULONG ulAutoInheritFlags = (SEF_DACL_AUTO_INHERIT |
SEF_SACL_AUTO_INHERIT |
SEF_AVOID_PRIVILEGE_CHECK |
SEF_AVOID_OWNER_CHECK);
TraceEnter(TRACE_NTFSSI, "CNTFSSecurity::GetDefaultSecurity");
TraceAssert(m_hItemList && DPA_GetPtrCount(m_hItemList));
dwErr = GetCurrentToken(TOKEN_QUERY, &hToken);
if (NOERROR != dwErr)
ExitGracefully(hr, HRESULT_FROM_WIN32(dwErr), "Unable to get current token");
// Only DACL and SACL get inherited
siLocal = si & ~(OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION);
if (siLocal)
{
hr = GetParentSecurity(si, &psdParent);
FailGracefully(hr, "Unable to get parent security descriptor");
}
#if(_WIN32_WINNT >= 0x0500)
if(!CreatePrivateObjectSecurityEx(psdParent,// Inherited ACEs come from here
NULL,
ppSD,
NULL,
!!(m_dwSIFlags & SI_CONTAINER),
ulAutoInheritFlags,
hToken, // owner & group come from here
&NTFSMap))
#else
if(!CreatePrivateObjectSecurity(psdParent,
NULL,
ppSD,
!!(m_dwSIFlags & SI_CONTAINER),
hToken,
&NTFSMap))
#endif
{
dwErr = GetLastError();
hr = HRESULT_FROM_WIN32(dwErr);
}
exit_gracefully:
if (hToken != INVALID_HANDLE_VALUE)
CloseHandle(hToken);
if (psdParent)
LocalFree(psdParent);
TraceLeaveResult(hr);
}
#endif // SUPPORT_NTFS_DEFAULT
#ifdef DONT_USE_ACLAPI
STDMETHODIMP
CNTFSSecurity::ReadObjectSecurity(LPCTSTR pszObject,
SECURITY_INFORMATION si,
PSECURITY_DESCRIPTOR *ppSD)
{
DWORD dwErr;
TraceEnter(TRACE_NTFSSI, "CNTFSSecurity::ReadObjectSecurity");
dwErr = NTFSReadSD(pszObject, si, ppSD);
TraceLeaveResult(HRESULT_FROM_WIN32(dwErr));
}
#endif // DONT_USE_ACLAPI
STDMETHODIMP
CNTFSSecurity::WriteObjectSecurity(LPCTSTR pszObject,
SECURITY_INFORMATION si,
PSECURITY_DESCRIPTOR pSD)
{
DWORD dwErr;
HRESULT hr = S_OK;
TraceEnter(TRACE_NTFSSI, "CNTFSSecurity::WriteObjectSecurity");
TraceAssert(pszObject != NULL);
TraceAssert(si != 0);
TraceAssert(pSD != NULL);
#ifdef DONT_USE_ACLAPI
//
// Assume that required privileges have already been
// enabled, if appropriate.
//
if (!SetFileSecurity(pszObject, si, pSD))
{
dwErr = GetLastError();
hr = HRESULT_FROM_WIN32(dwErr);
}
#else
hr = CSecurityInformation::WriteObjectSecurity(pszObject, si, pSD);
// This is a workaround. SetNamedSecurityInfo[Ex] fails with Access Denied
// in some cases where the owner is trying to set the DACL
// (typically because the propagation code can't enumerate the children
// because owner doesn't have read access).
if (E_ACCESSDENIED == hr)
{
SECURITY_DESCRIPTOR_CONTROL wControl = 0;
DWORD dwRevision;
// If we're setting a protected DACL (i.e. no inheritance from parent)
// try SetFileSecurity. If that works, try the full write again.
//
// Don't do this if the DACL isn't protected since it may fool the
// system into thinking that this is a downlevel DACL that should
// be protected. That would be very confusing to the user.
GetSecurityDescriptorControl(pSD, &wControl, &dwRevision);
if ((si & DACL_SECURITY_INFORMATION)
&& ((wControl & SE_DACL_PROTECTED) || (m_dwSIFlags & SI_NO_ACL_PROTECT))
&& SetFileSecurity(pszObject, si, pSD))
{
hr = CSecurityInformation::WriteObjectSecurity(pszObject, si, pSD);
}
}
#endif // DONT_USE_ACLAPI
//
// Notify the shell if we change permissions on a folder (48220)
//
if (SUCCEEDED(hr) &&
(si & DACL_SECURITY_INFORMATION) &&
(m_dwSIFlags & SI_CONTAINER))
{
SHChangeNotify(SHCNE_UPDATEDIR,
SHCNF_PATH | SHCNF_FLUSH | SHCNF_FLUSHNOWAIT,
pszObject,
NULL);
}
TraceLeaveResult(hr);
}
void
CNTFSSecurity::WaitForComparison()
{
if (m_hCompareThread != NULL)
{
DWORD dwResult;
HCURSOR hcurPrevious = SetCursor(LoadCursor(NULL, IDC_WAIT));
SetThreadPriority(m_hCompareThread, THREAD_PRIORITY_HIGHEST);
dwResult = WaitForSingleObject(m_hCompareThread, INFINITE);
if (m_pCompareData != NULL)
{
if (GetExitCodeThread(m_hCompareThread, &dwResult))
{
m_pCompareData->hrResult = dwResult;
}
else
{
dwResult = GetLastError();
m_pCompareData->hrResult = HRESULT_FROM_WIN32(dwResult);
}
}
CloseHandle(m_hCompareThread);
m_hCompareThread = NULL;
SetCursor(hcurPrevious);
}
}
DWORD WINAPI
CNTFSSecurity::NTFSReadSD(LPCTSTR pszObject,
SECURITY_INFORMATION si,
PSECURITY_DESCRIPTOR* ppSD)
{
DWORD dwLength = 0;
DWORD dwErr = 0;
TraceEnter(TRACE_NTFSSI | TRACE_NTFSCOMPARE, "CNTFSSecurity::NTFSReadSD");
TraceAssert(pszObject != NULL);
TraceAssert(si != 0);
TraceAssert(ppSD != NULL);
//
// Assume that required privileges have already been
// enabled, if appropriate.
//
GetFileSecurity(pszObject, si, NULL, 0, &dwLength);
if (dwLength)
{
*ppSD = LocalAlloc(LPTR, dwLength);
if (*ppSD &&
!GetFileSecurity(pszObject, si, *ppSD, dwLength, &dwLength))
{
dwErr = GetLastError();
LocalFree(*ppSD);
*ppSD = NULL;
}
}
else
dwErr = GetLastError();
TraceLeaveValue(dwErr);
}
DWORD WINAPI
CNTFSSecurity::NTFSCompareThreadProc(LPVOID pvData)
{
PNTFS_COMPARE_DATA pCompareData = (PNTFS_COMPARE_DATA)pvData;
HRESULT hr;
DWORD dwSIFlags;
BOOL bOwnerConflict = FALSE;
BOOL bSaclConflict = FALSE;
BOOL bDaclConflict = FALSE;
TraceEnter(TRACE_NTFSCOMPARE, "CNTFSSecurity::NTFSCompareThreadProc");
TraceAssert(pCompareData != NULL);
dwSIFlags = pCompareData->dwSIFlags;
hr = DPA_CompareSecurityIntersection(pCompareData->hItemList,
NTFSReadSD,
(dwSIFlags & SI_EDIT_OWNER) ? &bOwnerConflict : NULL,
NULL,
(dwSIFlags & SI_EDIT_AUDITS) ? &bSaclConflict : NULL,
&bDaclConflict,
NULL,
NULL,
&pCompareData->pszSaclConflict,
&pCompareData->pszDaclConflict,
&pCompareData->bAbortThread);
if (SUCCEEDED(hr))
{
if (bOwnerConflict)
pCompareData->siConflict |= OWNER_SECURITY_INFORMATION;
if (bSaclConflict)
pCompareData->siConflict |= SACL_SECURITY_INFORMATION;
if (bDaclConflict)
pCompareData->siConflict |= DACL_SECURITY_INFORMATION;
if (pCompareData->pszSaclConflict)
pCompareData->idsSaclPrompt = IDS_BAD_SACL_INTERSECTION;
if (pCompareData->pszDaclConflict)
pCompareData->idsDaclPrompt = IDS_BAD_DACL_INTERSECTION;
}
TraceLeaveResult(hr);
}
HRESULT
CNTFSSecurity::SetSecurityLocal(SECURITY_INFORMATION si,
PSECURITY_DESCRIPTOR pSD,
LPBOOL pbNotAllApplied)
{
HRESULT hr = S_OK;
HCURSOR hcur = NULL;
int i;
TraceEnter(TRACE_NTFSSI, "CNTFSSecurity::SetSecurityLocal");
TraceAssert(pSD != NULL);
TraceAssert(SI_CONTAINER & m_dwSIFlags);
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 = ERROR_SUCCESS;
NTFS_PF_DATA dataPF;
if( !si )
TraceLeaveResult(hr);
dataPF.si = si;
dataPF.pNTFSSec = this;
dataPF.pSD = pSD;
dataPF.bCancel = FALSE;
//
// Get pointers to various security descriptor parts for
// calling SetNamedSecurityInfo
//
GetSecurityDescriptorControl(pSD, &wSDControl, &dwRevision);
GetSecurityDescriptorOwner(pSD, &psidOwner, &bDefaulted);
GetSecurityDescriptorGroup(pSD, &psidGroup, &bDefaulted);
GetSecurityDescriptorDacl(pSD, &bPresent, &pDacl, &bDefaulted);
GetSecurityDescriptorSacl(pSD, &bPresent, &pSacl, &bDefaulted);
if (si & DACL_SECURITY_INFORMATION)
{
if (wSDControl & SE_DACL_PROTECTED)
si |= PROTECTED_DACL_SECURITY_INFORMATION;
else
si |= UNPROTECTED_DACL_SECURITY_INFORMATION;
}
if (si & SACL_SECURITY_INFORMATION)
{
if (wSDControl & SE_SACL_PROTECTED)
si |= PROTECTED_SACL_SECURITY_INFORMATION;
else
si |= UNPROTECTED_SACL_SECURITY_INFORMATION;
}
if (pbNotAllApplied)
*pbNotAllApplied = FALSE;
if (NULL == m_hItemList)
ExitGracefully(hr, E_UNEXPECTED, "CSecurityInformation not initialized");
hcur = SetCursor(LoadCursor(NULL, IDC_WAIT));
CreateProgressDialog(si);
for (i = 0; i < DPA_GetPtrCount(m_hItemList); i++)
{
LPTSTR pszItem = (LPTSTR)DPA_FastGetPtr(m_hItemList, i);
if (NULL != pszItem)
{
dwErr = TreeResetNamedSecurityInfo( pszItem,
SE_FILE_OBJECT,
si,
si & OWNER_SECURITY_INFORMATION ? psidOwner : NULL,
NULL,
si & DACL_SECURITY_INFORMATION ? pDacl : NULL,
si & SACL_SECURITY_INFORMATION ? pSacl : NULL,
FALSE,
ProgressFunction,
ProgressInvokeEveryObject,
(PVOID)&dataPF);
hr = HRESULT_FROM_WIN32(dwErr);
FailGracefully(hr, "Unable to recursively apply security");
if(dataPF.bCancel)
{
if(pbNotAllApplied)
*pbNotAllApplied = TRUE;
ExitGracefully(hr, S_FALSE, "User canceled the operation");
}
}
else
{
hr = E_UNEXPECTED;
break;
}
}
exit_gracefully:
CloseProgressDialog();
if (m_psdOwnerFullControl)
{
LocalFree(m_psdOwnerFullControl);
m_psdOwnerFullControl = NULL;
}
if (hcur)
{
SetCursor(hcur);
}
TraceLeaveResult(hr);
}
BOOL PathIsDotOrDotDot(LPCTSTR pszPath)
{
if (TEXT('.') == *pszPath++)
{
if (TEXT('\0') == *pszPath || (TEXT('.') == *pszPath && TEXT('\0') == *(pszPath + 1)))
return TRUE;
}
return FALSE;
}
typedef struct _APPLY_SECURITY_ERROR
{
HWND hwndParent;
DWORD dwError;
LPCTSTR pszPath;
UINT idMsg[1]; // Optional, string resource IDs (only 1 used so far)
} APPLY_SECURITY_ERROR;
INT_PTR CALLBACK
FailedApplySecurityProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch (uMsg)
{
case WM_INITDIALOG:
{
APPLY_SECURITY_ERROR* pae = (APPLY_SECURITY_ERROR*)lParam;
LPTSTR pszT = NULL;
// Set the message string(s)
for (int i = 0; i < ARRAYSIZE(pae->idMsg); i++)
{
if (pae->idMsg[i])
{
LoadStringAlloc(&pszT, g_hInstance, pae->idMsg[i]);
if (pszT)
SetDlgItemText(hDlg, (IDC_MSG1 + i), pszT);
LocalFreeString(&pszT);
}
}
// Compact the path so it fits on the dialog
PathSetDlgItemPath(hDlg, IDC_FILENAME, pae->pszPath);
// Set the error text
if (NOERROR != pae->dwError)
{
if (!GetSystemErrorText(&pszT, pae->dwError))
FormatStringID(&pszT, g_hInstance, IDS_FMT_UNKNOWN_ERROR, pae->dwError);
if (pszT)
SetDlgItemText(hDlg, IDC_ERROR_TXT, pszT);
LocalFreeString(&pszT);
}
break;
}
case WM_COMMAND:
{
WORD wControlID = GET_WM_COMMAND_ID(wParam, lParam);
switch (wControlID)
{
case IDOK:
case IDCANCEL:
EndDialog(hDlg, wControlID);
return TRUE;
}
break;
}
}
return FALSE;
}
//
// This function displays the "An error has occured [Continue] [Cancel]" message
//
// Returns IDOK or IDCANCEL
//
int
FailedApplySecurityErrorDlg(HWND hWndParent, APPLY_SECURITY_ERROR* pae)
{
//The progress dialog must be visible when this error message is shown
if( !IsWindowVisible( pae->hwndParent ) )
{
ShowWindow( pae->hwndParent, SW_SHOW);
SetForegroundWindow( pae->hwndParent );
}
return (int)DialogBoxParam(g_hInstance,
MAKEINTRESOURCE(IDD_SET_SECURITY_ERROR),
hWndParent,
FailedApplySecurityProc,
(LPARAM)pae);
}
#ifndef IDA_APPLYATTRIBS
// this is the resource ID of an AVI in shell32.dll. If shell32's
// resource ID's change, we'll get the wrong animation (or none).
// We could steal the AVI and build it into rshx32's resources, except
// it almost doubles the size of rshx32.dll (~35k to ~57k).
#define IDA_APPLYATTRIBS 165 // animation for applying file attributes
#endif
void
CNTFSSecurity::CreateProgressDialog(SECURITY_INFORMATION si)
{
HRESULT hr = S_OK;
// Shouldn't be necessary, but just in case
CloseProgressDialog();
// m_hwndOwner is the toplevel parent of the Security page
m_hwndPopupOwner = GetLastActivePopup(m_hwndOwner);
__try
{
hr = CoCreateInstance(CLSID_ProgressDialog,
NULL,
CLSCTX_INPROC_SERVER,
IID_IProgressDialog,
(void**)&m_pProgressDlg);
}
__except(EXCEPTION_EXECUTE_HANDLER)
{
hr = E_OUTOFMEMORY;
}
if (SUCCEEDED(hr) && m_pProgressDlg)
{
WCHAR szT[256];
UINT ids = IDS_RESET_SEC_TREE;
IOleWindow *pWindow;
LoadStringW(g_hInstance, IDS_PROPPAGE_TITLE, szT, ARRAYSIZE(szT));
m_pProgressDlg->SetTitle(szT);
switch (si)
{
case OWNER_SECURITY_INFORMATION:
ids = IDS_RESET_OWNER_TREE;
break;
case SACL_SECURITY_INFORMATION:
ids = IDS_RESET_SACL_TREE;
break;
case DACL_SECURITY_INFORMATION:
ids = IDS_RESET_DACL_TREE;
break;
}
LoadStringW(g_hInstance, ids, szT, ARRAYSIZE(szT));
m_pProgressDlg->SetLine(1, szT, FALSE, NULL);
m_pProgressDlg->SetAnimation(GetModuleHandle(TEXT("shell32.dll")), IDA_APPLYATTRIBS);
m_pProgressDlg->StartProgressDialog(m_hwndPopupOwner,
NULL,
PROGDLG_MODAL | PROGDLG_NOTIME
| PROGDLG_NOMINIMIZE | PROGDLG_NOPROGRESSBAR,
NULL);
if (SUCCEEDED(m_pProgressDlg->QueryInterface(IID_IOleWindow, (void**)&pWindow)))
{
pWindow->GetWindow(&m_hwndPopupOwner);
pWindow->Release();
}
}
}
void
CNTFSSecurity::CloseProgressDialog(void)
{
m_hwndPopupOwner = NULL;
if (m_pProgressDlg)
{
m_pProgressDlg->StopProgressDialog();
m_pProgressDlg->Release();
m_pProgressDlg = NULL;
}
}
HRESULT
CNTFSSecurity::SetProgress(LPTSTR pszFile)
{
USES_CONVERSION;
if (m_pProgressDlg)
{
m_pProgressDlg->SetLine(2, T2W(pszFile), TRUE, NULL);
if (m_pProgressDlg->HasUserCancelled())
return S_FALSE;
}
return S_OK;
}
HRESULT
CNTFSSecurity::BuildOwnerFullControlSD(PSECURITY_DESCRIPTOR pSD)
{
PSID psidOwner;
BOOL bDefaulted;
DWORD dwAclLen;
PACL pAcl;
PACE_HEADER pAce;
if (!GetSecurityDescriptorOwner(pSD, &psidOwner, &bDefaulted))
return E_INVALIDARG;
dwAclLen = sizeof(ACL)
+ sizeof(KNOWN_ACE) - sizeof(DWORD)
+ GetLengthSid(psidOwner);
m_psdOwnerFullControl = (PSECURITY_DESCRIPTOR)LocalAlloc(LPTR, SECURITY_DESCRIPTOR_MIN_LENGTH + dwAclLen);
if (NULL == m_psdOwnerFullControl)
return E_OUTOFMEMORY;
InitializeSecurityDescriptor(m_psdOwnerFullControl, SECURITY_DESCRIPTOR_REVISION);
pAcl = (PACL)ByteOffset(m_psdOwnerFullControl, SECURITY_DESCRIPTOR_MIN_LENGTH);
InitializeAcl(pAcl, dwAclLen, ACL_REVISION);
AddAccessAllowedAce(pAcl, ACL_REVISION, FILE_ALL_ACCESS, psidOwner);
pAce = (PACE_HEADER)FirstAce(pAcl);
pAce->AceFlags = INHERIT_FULL;
SetSecurityDescriptorDacl(m_psdOwnerFullControl, TRUE, pAcl, TRUE);
return S_OK;
}
VOID ProgressFunction(IN LPWSTR pObjectName,
IN DWORD Status,
IN OUT PPROG_INVOKE_SETTING pInvokeSetting ,
IN PVOID Args,
BOOL bSecuritySet)
{
TraceEnter(TRACE_NTFSCOMPARE, "ProgressFunction");
TraceAssert(pObjectName != NULL);
TraceAssert(Args);
PNTFS_PF_DATA pfData = (PNTFS_PF_DATA)(Args);
CNTFSSecurity * pNTFSSec = pfData->pNTFSSec;
HRESULT hr = S_OK;
if( Status == ERROR_SUCCESS )
{
//
// Notify the shell if we change permissions on a folder (48220)
//
if ( pfData->si & DACL_SECURITY_INFORMATION)
{
SHChangeNotify(SHCNE_UPDATEDIR,
SHCNF_PATH | SHCNF_FLUSH | SHCNF_FLUSHNOWAIT,
pObjectName,
NULL);
}
}
else
{
//
//This means it was able to set security on this folder and some error
//occured while enumerating child.
//
if(bSecuritySet && pfData->si & OWNER_SECURITY_INFORMATION)
{
BOOL bIsFile = FALSE;
hr = pNTFSSec->GiveOwnerFullControl(pObjectName, pfData->pSD, &bIsFile);
if(hr == S_OK)
{
//
//Look for comment in SetFileSecurityUsingNTName
//
*pInvokeSetting = bIsFile ?ProgressInvokeEveryObject:ProgressRetryOperation;
TraceLeaveVoid();
}
}
APPLY_SECURITY_ERROR ae = { ((PNTFS_PF_DATA)(Args))->pNTFSSec->GetHwndPopOwner(),HRESULT_FROM_WIN32(Status), pObjectName, { 0 } };
if (IDOK != FailedApplySecurityErrorDlg( ((PNTFS_PF_DATA)(Args))->pNTFSSec->GetHwndPopOwner(), &ae))
{
*pInvokeSetting = ProgressCancelOperation; // abort
pfData->bCancel = TRUE;
}
else
{
*pInvokeSetting = ProgressInvokeEveryObject; // continue
}
}
if (S_FALSE == ((PNTFS_PF_DATA)(Args))->pNTFSSec->SetProgress(pObjectName))
*pInvokeSetting = ProgressCancelOperation;
TraceLeaveVoid();
}
HRESULT CNTFSSecurity::GiveOwnerFullControl( LPCWSTR lpszFileName,
PSECURITY_DESCRIPTOR pSD,
BOOL *pbIsFile)
{
HRESULT hr = S_OK;
// Ask the user if they want to grant themselves access
if (!m_psdOwnerFullControl)
{
if (IDYES == MsgPopup(m_hwndPopupOwner,
MAKEINTRESOURCE(IDS_FMT_WRITE_OWNER_ERR),
MAKEINTRESOURCE(IDS_PROPPAGE_TITLE),
MB_YESNO | MB_ICONWARNING | MB_SETFOREGROUND,
g_hInstance,
lpszFileName))
{
BuildOwnerFullControlSD(pSD);
}
else
{
// Continue without enumerating this folder
TraceLeaveResult(S_FALSE);
}
}
if (m_psdOwnerFullControl)
{
// Give the owner Full Control
// Use SetFileSecurity instead?, yes Use SetFileSecurity instead
UNICODE_STRING usFileName;
RtlInitUnicodeString(&usFileName,lpszFileName);
if(!SetFileSecurityUsingNTName(&usFileName,
m_psdOwnerFullControl,
pbIsFile))
{
hr = E_FAIL;
}
if(SUCCEEDED(hr))
TraceLeaveResult(S_OK);
}
TraceLeaveResult(S_FALSE);
}
GENERIC_MAPPING STANDARD_FILE_MAP=
{
FILE_GENERIC_READ,
FILE_GENERIC_WRITE,
FILE_GENERIC_EXECUTE,
FILE_ALL_ACCESS
};
STDMETHODIMP
CNTFSSecurity::GetInheritSource( SECURITY_INFORMATION si,
PACL pACL,
PINHERITED_FROM *ppInheritArray)
{
HRESULT hr = S_OK;
LPTSTR pszItem;
DWORD dwErr = ERROR_SUCCESS;
PINHERITED_FROM pTempInherit = NULL;
PINHERITED_FROM pTempInherit2 = NULL;
LPWSTR pStrTemp = NULL;
TraceEnter(TRACE_SI, "CNTFSSecurity::GetInheritSource");
TraceAssert(pACL != 0);
TraceAssert(ppInheritArray != NULL);
if( pACL == NULL || ppInheritArray == NULL )
ExitGracefully(hr, E_POINTER, "Invalid Parameters, CNTFSSecurity::GetInheritSource");
// Get the name of the first item
pszItem = (LPTSTR)DPA_GetPtr(m_hItemList, 0);
if (NULL == pszItem)
ExitGracefully(hr, E_UNEXPECTED, "CSecurityInformation not initialized");
pTempInherit = (PINHERITED_FROM)LocalAlloc( LPTR, sizeof(INHERITED_FROM)*pACL->AceCount);
if(pTempInherit == NULL)
ExitGracefully(hr, E_OUTOFMEMORY,"OUT of Memory");
dwErr = GetInheritanceSource(pszItem,
SE_FILE_OBJECT,
si,
m_dwSIFlags & SI_CONTAINER,
NULL,
0,
pACL,
NULL,
&STANDARD_FILE_MAP,
pTempInherit);
hr = HRESULT_FROM_WIN32(dwErr);
FailGracefully( hr, "GetInheritanceSource Failed");
DWORD nSize;
UINT i;
nSize = sizeof(INHERITED_FROM)*pACL->AceCount;
for(i = 0; i < pACL->AceCount; ++i)
{
if(pTempInherit[i].AncestorName)
nSize += StringByteSize(pTempInherit[i].AncestorName);
}
pTempInherit2 = (PINHERITED_FROM)LocalAlloc( LPTR, nSize );
if(pTempInherit2 == NULL)
ExitGracefully(hr, E_OUTOFMEMORY,"OUT of Memory");
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);
TraceLeaveResult(hr);
}
BOOL SetFileSecurityUsingNTName(IN PUNICODE_STRING pFileName,
IN PSECURITY_DESCRIPTOR pSecurityDescriptor,
IN PBOOL pbIsFile)
{
NTSTATUS Status;
OBJECT_ATTRIBUTES Obja;
IO_STATUS_BLOCK IoStatusBlock;
HANDLE hFile = NULL;
InitializeObjectAttributes(
&Obja,
pFileName,
OBJ_CASE_INSENSITIVE,
NULL,
NULL
);
Status = NtOpenFile(
&hFile,
WRITE_DAC,
&Obja,
&IoStatusBlock,
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
FILE_OPEN_REPARSE_POINT
);
if ( Status == STATUS_INVALID_PARAMETER ) {
Status = NtOpenFile(
&hFile,
WRITE_DAC,
&Obja,
&IoStatusBlock,
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
0
);
}
if (!NT_SUCCESS(Status)) {
return FALSE;
}
if (!SetKernelObjectSecurity(
hFile,
DACL_SECURITY_INFORMATION,
pSecurityDescriptor
))
{
ASSERT(FALSE);
NtClose(hFile);
return FALSE;
}
NtClose(hFile);
//
//When resetting the owner, if user is not owner and he doesn't have any permissions
//TreeResetNamedSecurityInfo cannot determine if its a file or directory. So after
//setting the ownership TreeResetNamedSecurityInfo tries to enumerate the file, which fails
//as there is nothing to enumerate and TreeResetNamedSecurityInfo calls ProgressFunction
//which stamps a FullControl on the file and ask TreeResetNamedSecurityInfo to retry
//which again fails and we are infinte loop. The way to break this is ask TreeResetNamedSecurityInfo
//not to retry if its a file. which is what we are doing below. Ugly, yup.
//
//default we assume its a file
//if its a file and we assume its a dir, we are infinite loop
//if its a dir and we assume its a file, we skip that dir which
//is lesser evil
//
*pbIsFile = TRUE;
//
//Open the file for Generic_read
//
if (NT_SUCCESS(Status = NtOpenFile(
&hFile,
FILE_GENERIC_READ,
&Obja,
&IoStatusBlock,
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
0
)))
{
//
// Query the attributes for the file/dir.
// In case of error, assume that it is a dir.
//
FILE_BASIC_INFORMATION BasicFileInfo;
if (NT_SUCCESS(Status = NtQueryInformationFile(
hFile,
&IoStatusBlock,
&BasicFileInfo,
sizeof(BasicFileInfo),
FileBasicInformation)))
{
if(BasicFileInfo.FileAttributes & FILE_ATTRIBUTE_DIRECTORY)
*pbIsFile = FALSE;
}
NtClose(hFile);
}
return TRUE;
}