Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

2839 lines
94 KiB

//+---------------------------------------------------------------------------
//
// Microsoft Windows
// Copyright (C) Microsoft Corporation, 1994 - 1996.
//
// File: file.cxx
//
// Contents: Local file support functions
//
// History: 8/94 davemont Created
//
//----------------------------------------------------------------------------
#include <aclpch.hxx>
#pragma hdrstop
#include <ntprov.hxx>
#include <alsup.hxx>
#include <martaevt.h>
extern "C"
{
#include <lmdfs.h>
#include <stdio.h>
#include <seopaque.h>
#include <sertlp.h>
}
#define LMRDR L"\\Device\\LanmanRedirector"
#define WINDFS L"\\Device\\WinDfs"
GENERIC_MAPPING gFileGenMap = {FILE_GENERIC_READ,
FILE_GENERIC_WRITE,
FILE_GENERIC_EXECUTE,
FILE_ALL_ACCESS};
//+---------------------------------------------------------------------------
//
// Function: ConvertFileHandleToName
//
// Synopsis: Determines the file name for a handle. Issues an
// NtQueryInformationFile to determine the file name
//
// Arguments: [IN hFile] -- 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
ConvertFileHandleToName(IN HANDLE hFile,
OUT PWSTR *ppwszName)
{
DWORD dwErr = ERROR_SUCCESS;
CHECK_HEAP
//
// First, determine the size of the buffer we need...
//
HANDLE hRootDir = NULL;
BYTE pBuff[512];
ULONG cLen = 0;
POBJECT_NAME_INFORMATION pNI = NULL;
PWSTR pwszPath = NULL;
NTSTATUS Status = NtQueryObject(hFile,
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(hFile,
ObjectNameInformation,
pNI,
cLen,
NULL);
if(NT_SUCCESS(Status))
{
pwszPath = pNI->Name.Buffer;
acDebugOut((DEB_TRACE_HANDLE, "Path for handle 0x%lx: %ws\n",
hFile, pwszPath));
}
AccFree(pNI);
}
else
{
Status = STATUS_NO_MEMORY;
}
}
dwErr = RtlNtStatusToDosError(Status);
if(dwErr == ERROR_SUCCESS && pwszPath == NULL)
{
dwErr = ERROR_INVALID_HANDLE;
}
if (dwErr != ERROR_SUCCESS)
{
acDebugOut(( DEB_ERROR,
"Failed to get path for handle 0x%lx: %lu\n",
hFile, dwErr ));
ASSERT( dwErr == ERROR_SUCCESS );
}
}
else
{
pwszPath =((POBJECT_NAME_INFORMATION)pBuff)->Name.Buffer;
acDebugOut((DEB_TRACE_HANDLE, "Path for handle 0x%lx: %ws\n", hFile, pwszPath));
}
if(dwErr == ERROR_SUCCESS &&
_wcsnicmp(pwszPath,
LMRDR,
sizeof(LMRDR) / sizeof(WCHAR) - 1) == 0)
{
*ppwszName = (PWSTR)AccAlloc(sizeof(WCHAR) *
(wcslen(pwszPath + ((sizeof(LMRDR) - 1) / sizeof(WCHAR))) + 2));
if(*ppwszName == NULL)
{
dwErr = ERROR_NOT_ENOUGH_MEMORY;
}
else
{
swprintf(*ppwszName,
L"\\%ws",
pwszPath + (sizeof(LMRDR) / sizeof(WCHAR) - 1));
}
acDebugOut((DEB_TRACE_HANDLE, "returning path %ws as LM Rdr path\n",
*ppwszName ));
return(dwErr);
}
if(dwErr != ERROR_SUCCESS)
{
acDebugOut((DEB_ERROR,
"ConvertFileHandleToPath on 0x%lx failed with %lu\n",
hFile,
dwErr));
return(dwErr);
}
UNICODE_STRING UnicodeString;
OBJECT_ATTRIBUTES Attributes;
UCHAR Buffer[1024];
BOOL fFound = FALSE;
ULONG Context = 0;
POBJECT_DIRECTORY_INFORMATION DirInfo = NULL;
//
// Get a handle to the directory and iterate through that directory
// to find the object name.
//
RtlInitUnicodeString(&UnicodeString,
L"\\??");
InitializeObjectAttributes(&Attributes,
&UnicodeString,
OBJ_CASE_INSENSITIVE,
NULL,
NULL);
Status = NtOpenDirectoryObject(&hRootDir,
DIRECTORY_QUERY,
&Attributes);
if (!NT_SUCCESS(Status))
{
return(RtlNtStatusToDosError(Status));
}
//
// Get the entries in batches that will fit in a buffer of size
// BUFFERSIZE until we find the entry that we want
//
while (NT_SUCCESS(Status) && !fFound )
{
RtlZeroMemory(Buffer,
1024);
Status = NtQueryDirectoryObject(hRootDir,
(PVOID)&Buffer,
1024,
FALSE,
FALSE,
&Context,
NULL);
if(NT_SUCCESS(Status))
{
//
// Keep looking until we've examined all the entries in this
// batch or we find what we're looking for.
//
DirInfo = (POBJECT_DIRECTORY_INFORMATION)&Buffer[0];
while(!fFound && DirInfo->Name.Length != 0)
{
HANDLE LinkHandle;
UNICODE_STRING LinkTarget;
ASSERT( DirInfo != NULL );
ASSERT( DirInfo->Name.Length != 0 );
ASSERT( DirInfo->TypeName.Length != 0 );
acDebugOut((DEB_TRACE_HANDLE, "Checking dir entry %wZ\n",
&DirInfo->Name));
RtlInitUnicodeString(&UnicodeString,
DirInfo->Name.Buffer);
InitializeObjectAttributes(&Attributes,
&UnicodeString,
OBJ_CASE_INSENSITIVE,
hRootDir,
NULL);
Status = NtOpenSymbolicLinkObject(&LinkHandle,
SYMBOLIC_LINK_QUERY,
&Attributes);
if(NT_SUCCESS(Status))
{
WCHAR LinkTargetBuffer[1024];
memset(LinkTargetBuffer,0,1024 * sizeof(WCHAR));
LinkTarget.Buffer = LinkTargetBuffer;
LinkTarget.Length = 0;
LinkTarget.MaximumLength = sizeof(LinkTargetBuffer);
Status = NtQuerySymbolicLinkObject(LinkHandle,
&LinkTarget,
NULL);
if(NT_SUCCESS(Status))
{
acDebugOut((DEB_TRACE_HANDLE, "Symbolic link for %wZ: %wZ\n",
&DirInfo->Name, &LinkTarget));
if(_wcsnicmp(pwszPath,
LinkTargetBuffer,
LinkTarget.Length / sizeof(WCHAR)) == 0 &&
IS_FILE_PATH( DirInfo->Name.Buffer,
DirInfo->Name.Length / sizeof(WCHAR) ) )
{
fFound = TRUE;
*ppwszName = (PWSTR)AccAlloc((wcslen(DirInfo->Name.Buffer) +
wcslen(pwszPath + (LinkTarget.Length / sizeof(WCHAR))) +
1) * sizeof(WCHAR));
if(*ppwszName == NULL)
{
dwErr = ERROR_NOT_ENOUGH_MEMORY;
}
else
{
swprintf(*ppwszName,
L"%ws%ws",
DirInfo->Name.Buffer,
pwszPath + (LinkTarget.Length / sizeof(WCHAR)));
acDebugOut((DEB_TRACE_HANDLE, "Returning path %ws\n", *ppwszName ));
}
}
}
NtClose(LinkHandle);
}
DirInfo++;
}
}
}
if (!fFound)
{
if(Status != STATUS_NO_MORE_ENTRIES)
{
dwErr = RtlNtStatusToDosError(Status);
}
else
{
dwErr = ERROR_RESOURCE_NAME_NOT_FOUND;
}
}
if(hRootDir != NULL)
{
NtClose(hRootDir);
}
return(dwErr);
}
//+---------------------------------------------------------------------------
//
// Function: ReadFileSD
//
// Synopsis: Reads the file descriptor off of the given file handle
//
// Arguments: [IN hFile] -- The (open) handle of the file
// object
// [IN SeInfo] -- The security information to
// read
// [IN cKnownSize] -- If non-0, this is the size
// of the buffer to allocate
// for the SD. If 0, the buffer
// size is determined
// [OUT ppSD] -- Where the security descriptor
// is returned
//
// Returns: ERROR_SUCCESS -- Succcess
// ERROR_NOT_ENOUGH_MEMORY -- A memory allocation failed
//
// Notes: The returned memory must be freed with AccFree
//
//----------------------------------------------------------------------------
DWORD
ReadFileSD(IN HANDLE hFile,
IN SECURITY_INFORMATION SeInfo,
IN ULONG cKnownSize,
OUT PSECURITY_DESCRIPTOR *ppSD)
{
DWORD dwErr = ERROR_SUCCESS;
CHECK_HEAP
NTSTATUS Status;
ULONG cNeeded;
//
// If we don't know the size of the object, go ahead and determine it
//
if(cKnownSize == 0)
{
Status = NtQuerySecurityObject(hFile,
SeInfo,
*ppSD,
0,
&cNeeded);
if(!NT_SUCCESS(Status))
{
if(Status == STATUS_BUFFER_TOO_SMALL)
{
cKnownSize = cNeeded;
Status = STATUS_SUCCESS;
}
}
dwErr = RtlNtStatusToDosError(Status);
}
//
// Now, the actual read
//
if(dwErr == ERROR_SUCCESS)
{
*ppSD = (PISECURITY_DESCRIPTOR)AccAlloc(cKnownSize);
if(*ppSD == NULL)
{
dwErr = ERROR_NOT_ENOUGH_MEMORY;
}
else
{
Status = NtQuerySecurityObject(hFile,
SeInfo,
*ppSD,
cKnownSize,
&cNeeded);
}
}
return(dwErr);
}
//+---------------------------------------------------------------------------
//
// Function: IsFileContainer
//
// Synopsis: Determines if the file is a container (directory)
//
// Arguments: [IN Handle] -- the (open) handle of the file
// object
// [OUT pfIsContainer] -- flag indicating if the object
// is a container
//
// Returns: ERROR_SUCCESS -- Succcess
//
//----------------------------------------------------------------------------
DWORD
IsFileContainer(HANDLE Handle,
PBOOL pfIsContainer)
{
NTSTATUS ntstatus;
IO_STATUS_BLOCK iosb;
FILE_BASIC_INFORMATION basicfileinfo;
*pfIsContainer = FALSE;
//
// call NtQueryInformationFile to get basic file information
//
if (NT_SUCCESS(ntstatus = NtQueryInformationFile(Handle,
&iosb,
&basicfileinfo,
sizeof(FILE_BASIC_INFORMATION),
FileBasicInformation)))
{
*pfIsContainer =
(basicfileinfo.FileAttributes & FILE_ATTRIBUTE_DIRECTORY) ?
TRUE :
FALSE;
return(ERROR_SUCCESS);
}
else
{
return(RtlNtStatusToDosError(ntstatus));
}
}
//+---------------------------------------------------------------------------
//
// Function: IsFilePathLocalOrLM
//
// Synopsis: Determines if the path is that of a local object or a remote
// (network) object
//
// Arguments: [IN pwszFile] -- The file to check
//
// Returns: ERROR_SUCCESS -- Succcess
// ERROR_PATH_NOT_FOUND -- No such path exists
//
//----------------------------------------------------------------------------
DWORD
IsFilePathLocalOrLM(IN LPWSTR pwszFile)
{
DWORD dwErr = ERROR_SUCCESS;
BOOL fIsDfs = FALSE;
NTSTATUS Status;
if (pwszFile && wcsncmp(pwszFile, L"\\\\?\\", 4) == 0)
{
pwszFile += 4;
}
//
// First, try the simply case of it not being accessible...
//
if(GetFileAttributes(pwszFile) == 0xFFFFFFFF)
{
dwErr = GetLastError();
if(dwErr == ERROR_PATH_NOT_FOUND || dwErr == ERROR_FILE_NOT_FOUND)
{
return(ERROR_PATH_NOT_FOUND);
}
}
//
// Otherwise, we need to find out whether it's a path that only we have
// access to or not
//
#if 0
// for some reason, the full path name built is never used - waste time
// First, we'll see if it's a relative path. If so, we'll have to
// build a full path...
//
PWSTR pwszFullPath = pwszFile;
DWORD dwSize;
if(wcslen(pwszFile) < 2 || (pwszFile[1] != L':' && pwszFile[1] != L'\\'))
{
//
// It's a relative path...
//
dwSize = GetFullPathName(pwszFile,
0,
NULL,
NULL);
if(dwSize == 0)
{
dwErr = GetLastError();
}
else
{
pwszFullPath = (PWSTR)AccAlloc((dwSize + 1) * sizeof(WCHAR));
if(pwszFullPath == NULL)
{
dwErr = ERROR_NOT_ENOUGH_MEMORY;
}
else
{
PWSTR pwszFilePart;
if(GetFullPathName(pwszFile,
dwSize,
pwszFullPath,
&pwszFilePart) == 0)
{
dwErr = GetLastError();
}
}
}
}
#endif
if(pwszFile[1] == L':')
{
if(GetDriveType(pwszFile) == DRIVE_REMOTE)
{
//
// Have to figure out what it is...
//
#define BUFFERSIZE 1024
HANDLE hRootDir;
UNICODE_STRING ObjDir;
OBJECT_ATTRIBUTES Attributes;
UCHAR Buffer[BUFFERSIZE];
ULONG Context = 0;
POBJECT_DIRECTORY_INFORMATION pDirInfo = NULL;
RtlInitUnicodeString(&ObjDir,
L"\\??");
InitializeObjectAttributes(&Attributes,
&ObjDir,
OBJ_CASE_INSENSITIVE,
NULL,
NULL);
Status = NtOpenDirectoryObject(&hRootDir,
DIRECTORY_QUERY,
&Attributes);
//
// Get the entries in batches that will fit in a buffer of size
// BUFFERSIZE until we find the entry that we want
//
BOOL fFound = FALSE;
while (NT_SUCCESS(Status) && !fFound )
{
RtlZeroMemory(Buffer,
BUFFERSIZE);
Status = NtQueryDirectoryObject(hRootDir,
(PVOID)&Buffer,
BUFFERSIZE,
FALSE,
FALSE,
&Context,
NULL);
if(NT_SUCCESS(Status))
{
//
// Keep looking until we've examined all the entries in this
// batch or we find what we're looking for.
//
pDirInfo = (POBJECT_DIRECTORY_INFORMATION)&Buffer[0];
while(pDirInfo->Name.Length != 0)
{
ULONG cChar;
cChar = pDirInfo->Name.Length/sizeof(WCHAR);
if(_wcsnicmp(pDirInfo->Name.Buffer,
pwszFile,
2) == 0)
{
fFound = TRUE;
break;
}
else
{
pDirInfo++;
}
}
}
}
NtClose(hRootDir);
if(fFound != TRUE)
{
dwErr = RtlNtStatusToDosError(Status);
if(dwErr == ERROR_SUCCESS)
{
dwErr = ERROR_PATH_NOT_FOUND;
}
}
else
{
//
// Now, figure out what type of path I have
//
if(wcscmp(pDirInfo->TypeName.Buffer,
L"SymbolicLink") == 0)
{
HANDLE hLink;
RtlInitUnicodeString(&ObjDir,
pDirInfo->Name.Buffer);
InitializeObjectAttributes(&Attributes,
&ObjDir,
OBJ_CASE_INSENSITIVE,
NULL,
NULL);
Status = NtOpenSymbolicLinkObject(&hLink,
SYMBOLIC_LINK_QUERY,
&Attributes);
if(NT_SUCCESS(Status))
{
UNICODE_STRING Link;
Link.Buffer = (PWSTR)Buffer;
Link.Length = 0;
Link.MaximumLength = sizeof(Buffer);
Status = NtQuerySymbolicLinkObject(hLink,
&Link,
NULL);
NtClose(hLink);
if(NT_SUCCESS(Status))
{
//
// See if this is part of the lanman redir set
//
if(_wcsnicmp(Link.Buffer,
LMRDR,
sizeof(LMRDR) / sizeof(WCHAR)) != 0)
{
//
// See if it's a DFS path before passing
// judgement
//
if(_wcsnicmp(Link.Buffer,
WINDFS,
sizeof(WINDFS) / sizeof(WCHAR)) == 0)
{
// fIsDfs = TRUE;
}
else
{
dwErr = ERROR_PATH_NOT_FOUND;
}
}
}
}
}
}
}
}
if(fIsDfs == TRUE || IS_UNC_PATH(pwszFile, wcslen(pwszFile)))
{
//
// Try and open it...
//
/*
//
// First, see if it's a DFS path...
//
if(fIsDfs || IsThisADfsPath((LPCWSTR)pwszFile, 0) == TRUE)
{
ULONG cLocals = 0;
dwErr = GetLMDfsPaths(pwszFile,
&cLocals,
NULL);
if(dwErr == ERROR_SUCCESS && cLocals == 0)
{
dwErr = ERROR_PATH_NOT_FOUND;
}
}
else
{
*/
//
// We'll try to open it...
//
UNICODE_STRING FileName;
if ( RtlDosPathNameToNtPathName_U(pwszFile,
&FileName,
NULL,
NULL) ) {
/*
WCHAR wszPath[MAX_PATH + 1 + sizeof(LMRDR) / sizeof(WCHAR) + 1];
//
// Build the path...
//
ASSERT(wcslen(pwszFile) < MAX_PATH + 1);
swprintf(wszPath,
L"%ws%ws",
LMRDR,
pwszFile + 1);
*/
// UNICODE_STRING Path;
OBJECT_ATTRIBUTES ObjAttribs;
IO_STATUS_BLOCK IOSb;
HANDLE hRmt;
// RtlInitUnicodeString(&Path, wszPath);
InitializeObjectAttributes(&ObjAttribs,
&FileName, // &Path,
OBJ_CASE_INSENSITIVE,
NULL,
NULL);
Status = NtCreateFile(&hRmt,
SYNCHRONIZE,
&ObjAttribs,
&IOSb,
NULL,
FILE_ATTRIBUTE_NORMAL,
FILE_SHARE_READ,
FILE_OPEN_IF,
FILE_SYNCHRONOUS_IO_NONALERT,
NULL,
0);
if(!NT_SUCCESS(Status))
{
dwErr = RtlNtStatusToDosError(Status);
}
else
{
NtClose(hRmt);
}
RtlFreeHeap(RtlProcessHeap(), 0, FileName.Buffer );
} else {
dwErr = ERROR_INVALID_NAME;
}
}
#if 0
//
// never used!!! Free our memory
//
if(pwszFullPath != pwszFile)
{
AccFree(pwszFullPath);
}
#endif
return(dwErr);
}
//+---------------------------------------------------------------------------
//
// Function : OpenFileObject
//
// Synopsis : opens the specified file (or directory) object
//
// Arguments: [IN pObjectName] -- The name of the file object
// [IN AccessMask] -- How to open the file
// [OUT Handle] -- Where to return the file
// handle
// [IN fOpenRoot] -- Open the path as the root of a drive
//
// Returns: ERROR_SUCCESS -- Success
//
//----------------------------------------------------------------------------
DWORD
OpenFileObject(IN LPWSTR pObjectName,
IN ACCESS_MASK AccessMask,
OUT PHANDLE Handle,
IN BOOL fOpenRoot)
{
acDebugOut((DEB_TRACE, "in OpenFileObject\n"));
NTSTATUS ntstatus;
DWORD status = ERROR_SUCCESS;
WCHAR PathBuff[ 7 ];
OBJECT_ATTRIBUTES oa;
IO_STATUS_BLOCK isb;
UNICODE_STRING FileName;
RTL_RELATIVE_NAME RelativeName;
IO_STATUS_BLOCK IoStatusBlock;
PVOID FreeBuffer = NULL;
//
// cut and paste code from windows\base\advapi\security.c SetFileSecurityW
//
if(fOpenRoot == TRUE && wcslen(pObjectName) == 2)
{
wcscpy(PathBuff, L"\\??\\");
wcscat(PathBuff, pObjectName);
RtlInitUnicodeString(&FileName, PathBuff);
RtlZeroMemory(&RelativeName, sizeof(RTL_RELATIVE_NAME));
} else {
if(RtlDosPathNameToNtPathName_U(pObjectName, &FileName, NULL, &RelativeName))
{
FreeBuffer = FileName.Buffer;
if ( RelativeName.RelativeName.Length )
{
FileName = *(PUNICODE_STRING)&RelativeName.RelativeName;
}
else
{
RelativeName.ContainingDirectory = NULL;
}
}
else
{
status = ERROR_INVALID_NAME;
}
}
if(status == ERROR_SUCCESS)
{
InitializeObjectAttributes(&oa,
&FileName,
OBJ_CASE_INSENSITIVE,
RelativeName.ContainingDirectory,
NULL);
ntstatus = NtOpenFile( Handle,
AccessMask,
&oa,
&isb,
FILE_SHARE_READ |
FILE_SHARE_WRITE |
FILE_SHARE_DELETE,
0);
if (!NT_SUCCESS(ntstatus))
{
status = RtlNtStatusToDosError(ntstatus);
}
RtlFreeHeap(RtlProcessHeap(), 0,FreeBuffer);
}
else
{
status = ERROR_INVALID_NAME;
}
acDebugOut((DEB_TRACE, "OutOpenFileObject: %lu\n", status));
return(status);
}
//+---------------------------------------------------------------------------
//
// Function: ReadFilePropertyRights
//
// Synopsis: Reads the access rights from the specified properties on the
// specified file
//
// Arguments: [IN pwszFile] -- The file 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
//
//----------------------------------------------------------------------------
DWORD
ReadFilePropertyRights(IN PWSTR pwszFile,
IN PACTRL_RIGHTS_INFO pRightsList,
IN ULONG cRights,
IN CAccessList& AccessList)
{
acDebugOut((DEB_TRACE,
"in ReadFilePropertyRights\n"));
DWORD dwErr = ERROR_SUCCESS;
PWSTR pwszPath;
CHECK_HEAP
//
// For the moment, there is only file properties itself...
//
ASSERT(cRights == 1 && pRightsList[0].pwszProperty == NULL);
if(cRights != 1 || pRightsList[0].pwszProperty != NULL)
{
return(ERROR_INVALID_PARAMETER);
}
//
// Set the server name to lookup accounts on
//
dwErr = SetAccessListLookupServer( pwszFile,
AccessList );
//
// Always open with READ_CONTROL..
//
HANDLE hFile;
if(dwErr == ERROR_SUCCESS)
{
dwErr = OpenFileObject(pwszFile,
GetDesiredAccess(READ_ACCESS_RIGHTS,
pRightsList[0].SeInfo) |
FILE_READ_ATTRIBUTES |
READ_CONTROL,
&hFile,
FALSE);
}
if(dwErr == ERROR_SUCCESS && IS_FILE_PATH(pwszFile,wcslen(pwszFile)))
{
//
// See if there is a server associated with this path
//
dwErr = ConvertFileHandleToName(hFile,
&pwszPath );
if(dwErr == ERROR_SUCCESS)
{
dwErr = SetAccessListLookupServer( pwszPath,
AccessList );
AccFree(pwszPath);
}
//
// jinhuang: do not care if it finds the remote server or not
//
dwErr = ERROR_SUCCESS;
}
if(dwErr == ERROR_SUCCESS)
{
PSECURITY_DESCRIPTOR pSD = NULL;
dwErr = GetKernelSecurityInfo(hFile,
pRightsList[0].SeInfo,
NULL,
NULL,
&pSD);
if(dwErr == ERROR_SUCCESS && pSD == NULL)
{
dwErr = ERROR_ACCESS_DENIED;
}
//
// 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.
// We only have to do this if we are reading the DACL or SACL
//
if(dwErr == ERROR_SUCCESS)
{
if((FLAG_ON(pRightsList[0].SeInfo, DACL_SECURITY_INFORMATION) &&
!FLAG_ON(((PISECURITY_DESCRIPTOR)pSD)->Control,
SE_DACL_AUTO_INHERITED | SE_DACL_PROTECTED)) ||
(FLAG_ON(pRightsList[0].SeInfo, SACL_SECURITY_INFORMATION) &&
!FLAG_ON(((PISECURITY_DESCRIPTOR)pSD)->Control,
SE_SACL_AUTO_INHERITED | SE_SACL_PROTECTED)))
{
//
// Ok, it's downlevel, so get the parent SD...
//
PSECURITY_DESCRIPTOR pParentSD;
dwErr = GetFileParentRights(pwszFile,
pRightsList,
cRights,
NULL,
NULL,
&pParentSD);
//
// gross hack for the NTFS people, who don't allow opens on the $Extend directory
//
if ( dwErr == ERROR_ACCESS_DENIED &&
_wcsnicmp( pwszFile + 1, L":\\$Extend", 9 ) == 0 ) {
pParentSD = NULL;
dwErr = ERROR_SUCCESS;
}
//
// 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 = GetKernelSecurityInfo(hFile,
pRightsList[0].SeInfo |
OWNER_SECURITY_INFORMATION |
GROUP_SECURITY_INFORMATION,
NULL,
NULL,
&pSD);
}
//
// A NULL parent SD means this object has no parent!
//
if(dwErr == ERROR_SUCCESS)
{
if(pParentSD != NULL)
{
BOOL fIsContainer;
dwErr = IsFileContainer(hFile,
&fIsContainer);
if(dwErr == ERROR_SUCCESS)
{
PSECURITY_DESCRIPTOR pNewSD;
dwErr = ConvertToAutoInheritSD(pParentSD,
pSD,
fIsContainer,
&gFileGenMap,
&pNewSD);
if(dwErr == ERROR_SUCCESS)
{
dwErr = AccessList.AddSD(pNewSD,
pRightsList[0].SeInfo,
pRightsList[0].pwszProperty);
DestroyPrivateObjectSecurity(&pNewSD);
}
}
AccFree(pParentSD);
}
else
{
dwErr = AccessList.AddSD(pSD,
pRightsList[0].SeInfo,
pRightsList[0].pwszProperty);
}
}
}
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);
}
NtClose(hFile);
}
acDebugOut((DEB_TRACE,
"Out ReadFilePropertyRights %lu\n",
dwErr));
return(dwErr);
}
//+---------------------------------------------------------------------------
//
// Function: ReadFileRights
//
// Synopsis: Reads the access rights from the specified properties on the
// open file
//
// Arguments: [IN pwszFile] -- The file 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
//
//----------------------------------------------------------------------------
DWORD
ReadFileRights(IN HANDLE hObject,
IN PACTRL_RIGHTS_INFO pRightsList,
IN ULONG cRights,
IN CAccessList& AccessList)
{
acDebugOut((DEB_TRACE,
"in ReadFileRights\n"));
DWORD dwErr = ERROR_SUCCESS;
PACL pDAcl, pSAcl;
PSECURITY_DESCRIPTOR pSD;
PWSTR pwszPath = NULL;
CHECK_HEAP
//
// See if there is a server associated with this path
//
dwErr = ConvertFileHandleToName(hObject,
&pwszPath );
if(dwErr == ERROR_SUCCESS)
{
dwErr = SetAccessListLookupServer( pwszPath,
AccessList );
AccFree(pwszPath);
}
if(dwErr == ERROR_SUCCESS)
{
dwErr = GetKernelSecurityInfo(hObject,
pRightsList[0].SeInfo,
&pDAcl,
&pSAcl,
&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 && !FLAG_ON(pRightsList[0].SeInfo,
DACL_SECURITY_INFORMATION |
SACL_SECURITY_INFORMATION))
{
//
// Just insert it and continue
//
dwErr = AccessList.AddSD(pSD,
pRightsList[0].SeInfo,
pRightsList[0].pwszProperty);
AccFree(pSD);
}
else if(dwErr == ERROR_SUCCESS)
{
if(!FLAG_ON(((PISECURITY_DESCRIPTOR)pSD)->Control,
SE_SACL_AUTO_INHERITED |
SE_DACL_AUTO_INHERITED))
{
//
// Ok, it's downlevel, so get the parent SD... In order to
// do this, we'll have to determine who the parent is (path wise)
//
PWSTR pwszName = NULL;
dwErr = ConvertFileHandleToName(hObject,
&pwszName);
if(dwErr == ERROR_SUCCESS)
{
PSECURITY_DESCRIPTOR pParentSD;
PACL pParentDAcl, pParentSAcl;
dwErr = GetFileParentRights(pwszName,
pRightsList,
cRights,
&pParentDAcl,
&pParentSAcl,
&pParentSD);
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 = GetKernelSecurityInfo(hObject,
pRightsList[0].SeInfo |
OWNER_SECURITY_INFORMATION |
GROUP_SECURITY_INFORMATION,
NULL,
NULL,
&pSD);
}
//
// A NULL parent SD means this object has no parent!
//
if(dwErr == ERROR_SUCCESS && pParentSD != NULL)
{
BOOL fIsContainer;
dwErr = IsFileContainer(hObject,
&fIsContainer);
if(dwErr == ERROR_SUCCESS)
{
PSECURITY_DESCRIPTOR pNewSD;
dwErr = ConvertToAutoInheritSD(pParentSD,
pSD,
fIsContainer,
&gFileGenMap,
&pNewSD);
if(dwErr == ERROR_SUCCESS)
{
dwErr = AccessList.AddSD(pNewSD,
pRightsList[0].SeInfo,
pRightsList[0].pwszProperty);
DestroyPrivateObjectSecurity(&pNewSD);
}
}
AccFree(pParentSD);
}
AccFree(pwszName);
}
}
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);
}
acDebugOut((DEB_TRACE,
"Out ReadFileRights %lu\n",
dwErr));
return(dwErr);
}
//+---------------------------------------------------------------------------
//
// Function: GetFileParentRights
//
// 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.
//
// Arguments: [IN pwszFile] -- The file/directory to get the
// parent for
// [IN pRightsList] -- The properties to get the
// rights for
// [IN cRights] -- Number of items in rights list
// [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
GetFileParentRights(IN LPWSTR pwszFile,
IN PACTRL_RIGHTS_INFO pRightsList,
IN ULONG cRights,
OUT PACL *ppDAcl,
OUT PACL *ppSAcl,
OUT PSECURITY_DESCRIPTOR *ppSD)
{
acDebugOut((DEB_TRACE, "in GetFileParentRights\n"));
DWORD dwErr = ERROR_SUCCESS;
BOOL fNoParent = FALSE;
WCHAR pwszLocalFileNameBuffer[MAX_PATH+1];
PWSTR pwszLocalFileName = (PWSTR) pwszLocalFileNameBuffer;
ULONG filesize;
PWSTR pwszLastComp;
CHECK_HEAP
if (0 == (filesize = RtlGetFullPathName_U(pwszFile, sizeof(WCHAR)*MAX_PATH, pwszLocalFileName, NULL)))
{
dwErr = ERROR_FILE_NOT_FOUND;
goto FileCleanup;
}
if (filesize > sizeof(WCHAR)*MAX_PATH)
{
//
// The buffer is too small. We have to allocate more.
//
if (NULL == (pwszLocalFileName = (PWSTR) AccAlloc(sizeof(WCHAR)+filesize)))
{
dwErr = ERROR_NOT_ENOUGH_MEMORY;
goto FileCleanup;
}
//
// Try to get the full pathname again. This time the buffer is big enough to hold the full pathname.
//
if (0 == (RtlGetFullPathName_U(pwszFile, filesize, pwszLocalFileName, NULL)))
{
dwErr = ERROR_FILE_NOT_FOUND;
goto FileCleanup;
}
}
//
// First, we have to figure out who are parent is. For now, since there
// are no supported properties, we'll simply open the parent object
//
pwszLastComp = wcsrchr(pwszLocalFileName, 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
//
WCHAR wcLast;
// Leave the trailing \, it doesn't hurt to have them on directories.
// Plus, we don't end up querying security on the Device (x:) instead
// of the intended root (x:\)
//
// We restore
pwszLastComp ++;
wcLast = *pwszLastComp;
*pwszLastComp = L'\0';
//
// Make sure if we were looking at the root of a share, that we don't try and go to far
//
if (IS_UNC_PATH(pwszLocalFileName, wcslen(pwszLocalFileName)))
{
//
//
// Have to pass "\\server" if the original string was "\\server\share"
//
*(pwszLastComp-1) = L'\0';
if(wcsrchr(pwszLocalFileName+2, L'\\') == NULL)
{
//
// It's impossible for us to have a parent, so return
//
*ppSD = NULL;
fNoParent = TRUE;
}
//
// Restore the '\'
//
*(pwszLastComp-1) = L'\\';
}
if(fNoParent == FALSE)
{
HANDLE hFile;
SECURITY_INFORMATION SeInfo = pRightsList[0].SeInfo;
//
// Don't want owner or group
//
SeInfo &= ~(OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION);
dwErr = OpenFileObject(pwszLocalFileName,
GetDesiredAccess(READ_ACCESS_RIGHTS,SeInfo),
&hFile,
TRUE);
if(dwErr == ERROR_SUCCESS)
{
dwErr = GetKernelSecurityInfo(hFile,
SeInfo,
NULL,
NULL,
ppSD);
if(dwErr == ERROR_SUCCESS)
{
//
// Convert it to self relative
//
PSECURITY_DESCRIPTOR pNewSD;
dwErr = MakeSDSelfRelative(*ppSD,
&pNewSD,
ppDAcl,
ppSAcl);
if(dwErr == ERROR_SUCCESS)
{
*ppSD = pNewSD;
}
else
{
AccFree(*ppSD);
}
}
NtClose(hFile);
}
}
*pwszLastComp = wcLast;
}
FileCleanup:
if ((pwszLocalFileName != (PWSTR) pwszLocalFileNameBuffer) && (NULL != pwszLocalFileName))
{
AccFree(pwszLocalFileName);
}
acDebugOut((DEB_TRACE, "Out GetFileParentRights: %lu\n", dwErr));
return(dwErr);
}
//+---------------------------------------------------------------------------
//
// Function: SetFilePropertyRights
//
// Synopsis: Sets the specified security info on the specified file object
// property
//
// Arguments: [IN hFile] -- The handle to the open object
// [IN SeInfo] -- Flag indicating what security
// info to set
// [IN pwszProperty] -- Property to set it on
// [IN pSD] -- The security desciptor to set
//
// Returns: ERROR_SUCCESS -- Success
// ERROR_INVALID_PARAMETER -- A bad paramter was given
//
//----------------------------------------------------------------------------
DWORD
SetFilePropertyRights(IN HANDLE hFile,
IN SECURITY_INFORMATION SeInfo,
IN PWSTR pwszProperty,
IN PSECURITY_DESCRIPTOR pSD)
{
acDebugOut((DEB_TRACE, "in SetFilePropertyRights\n"));
DWORD dwErr = ERROR_SUCCESS;
//
// Filesystems don't support properties yet...
//
ASSERT(pwszProperty == NULL);
if(pwszProperty != NULL)
{
return(ERROR_INVALID_PARAMETER);
}
//
// Marta only writes uplevel security descriptors.
//
// The caller of SetFilePropertyRights will call with SE_xACL_AUTO_INHERITED off in those
// cases that it wants the underlying file system to do auto inheritance.
// The caller of SetFilePropertyRights will call with SE_xACL_AUTO_INHERITED on in those
// cases that it wants the underlying file system 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;
}
//
// Otherwise, do the set
//
NTSTATUS Status = NtSetSecurityObject(hFile,
SeInfo,
pSD);
dwErr = RtlNtStatusToDosError(Status);
acDebugOut((DEB_TRACE,
"Out SetFilePropertyRights: %ld\n",
dwErr));
return(dwErr);
}
#define CLEANUP_ON_INTERRUPT(pstopflag) \
if(*pstopflag != 0) \
{ \
goto FileCleanup; \
}
//+---------------------------------------------------------------------------
//
// Function: SetAndPropagateFilePropertyRights
//
// Synopsis: Sets the speecified access on the object and, if appropriate,
// propagates the access to the apporpriate child objects
//
// Arguments: [IN pwszFile] -- The path to set and propagate
// [IN pwszProperty] -- Property to set it on
// [IN RootAccList] -- CAccessList that indicates
// what access is to be set on
// the object
// [IN pfStopFlag] -- Address of the stop flag
// to be monitored
// [IN pcProcessed] -- Count of processed items
// [IN hOpenObject] -- OPTIONAL handle to the file object
// if it's already open
//
// Returns: ERROR_SUCCESS -- Success
// ERROR_INVALID_PARAMETER -- A bad paramter was given
//
//----------------------------------------------------------------------------
DWORD
SetAndPropagateFilePropertyRights(IN PWSTR pwszFile,
IN PWSTR pwszProperty,
IN CAccessList& RootAccList,
IN PULONG pfStopFlag,
IN PULONG pcProcessed,
IN HANDLE hOpenObject OPTIONAL)
{
acDebugOut((DEB_TRACE, "in SetAndPropagateFilePropertyRights\n"));
DWORD dwErr = ERROR_SUCCESS;
PSECURITY_DESCRIPTOR pReadSD = NULL;
HANDLE hObject = NULL;
BOOL fManualProp = FALSE;
NTSTATUS Status;
ULONG cNeeded = 0;
BOOL fIsCont = FALSE;
ULONG fProtected = 0;
HANDLE hProcessToken;
PSECURITY_DESCRIPTOR pSD = NULL;
SECURITY_INFORMATION SeInfo = 0;
PSECURITY_DESCRIPTOR pUpdateSD = NULL;
CSList FailureLogList(FreePropagationFailureListEntry);
//
// First, get the security descriptor
//
dwErr = RootAccList.BuildSDForAccessList(&pSD,
&SeInfo,
ACCLIST_SD_ABSOK);
if(dwErr == ERROR_SUCCESS &&
FLAG_ON(SeInfo, DACL_SECURITY_INFORMATION | SACL_SECURITY_INFORMATION))
{
//
// Next, open it
//
if(hOpenObject == NULL)
{
dwErr = OpenFileObject(pwszFile,
GetDesiredAccess(MODIFY_ACCESS_RIGHTS,
SeInfo) | FILE_READ_ATTRIBUTES | READ_CONTROL,
&hObject,
FALSE);
}
else
{
hObject = hOpenObject;
}
if(dwErr == ERROR_SUCCESS)
{
dwErr = IsFileContainer(hObject,
&fIsCont);
}
if(dwErr != ERROR_SUCCESS || *pfStopFlag != 0)
{
goto FileCleanup;
}
//
// Ok, first, we have to read the SD off the object. We do this
// so that we can determine what the potential inheritance is for
// our children following the object getting an updated security
// descriptor.
//
if(dwErr == ERROR_SUCCESS && fIsCont == TRUE)
{
dwErr = ReadFileSD(hObject,
SeInfo,
0,
&pReadSD);
}
//
// Now, write the current SD out to the object. Note that it's being
// written out as an uplevel acl
//
if(dwErr == ERROR_SUCCESS)
{
CLEANUP_ON_INTERRUPT(pfStopFlag);
dwErr = SetFilePropertyRights(hObject,
SeInfo,
pwszProperty,
pSD);
if(dwErr == ERROR_SUCCESS)
{
PSECURITY_DESCRIPTOR pVerifySD;
(*pcProcessed)++;
CLEANUP_ON_INTERRUPT(pfStopFlag);
//
// Now, we have to reread the new SD, to see if we need
// to do manual propagation
//
dwErr = ReadFileSD(hObject,
SeInfo,
RootAccList.QuerySDSize(),
&pVerifySD);
//
// Get our process token
//
if(dwErr == ERROR_SUCCESS)
{
dwErr = GetCurrentToken(&hProcessToken);
}
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(pISD->Control, SE_DACL_PROTECTED)) &&
!(FLAG_ON(SeInfo, SACL_SECURITY_INFORMATION) &&
FLAG_ON(pISD->Control, SE_SACL_AUTO_INHERITED &&
!FLAG_ON(pISD->Control, SE_SACL_PROTECTED))))
{
//
// 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)) &&
fIsCont == TRUE)
{
fManualProp = TRUE;
}
//
// Upgrade it...
//
dwErr = UpdateFileSDByPath(pSD,
pwszFile,
hObject,
hProcessToken,
SeInfo,
fIsCont,
&pUpdateSD);
//
// 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 = pUpdateSD;
if(fManualProp == TRUE)
{
pWriteSD = pReadSD;
}
//
// Reset it...
//
if(dwErr == ERROR_SUCCESS)
{
dwErr = SetFilePropertyRights(hObject,
SeInfo,
pwszProperty,
pWriteSD);
}
}
AccFree(pVerifySD);
}
CLEANUP_ON_INTERRUPT(pfStopFlag);
}
}
//
// Ok, now we'll do the right thing
//
if(dwErr == ERROR_SUCCESS && fManualProp == TRUE)
{
//
// We'll have to do our own propagation...
//
PSECURITY_DESCRIPTOR pUpdateParentSD = NULL;
if(!FLAG_ON(SeInfo, OWNER_SECURITY_INFORMATION) ||
!FLAG_ON(SeInfo, GROUP_SECURITY_INFORMATION))
{
dwErr = GetKernelSecurityInfo(hObject,
SeInfo |
OWNER_SECURITY_INFORMATION |
GROUP_SECURITY_INFORMATION,
NULL,
NULL,
&pUpdateParentSD);
}
//
// Ok, go ahead and do deep. This will possibly save us
// some storage space in the long run...
//
if(dwErr == ERROR_SUCCESS)
{
//
// Set our protected flags. If we aren't messing with a particular acl, we'll
// pretend it's protected
//
fProtected = ((SECURITY_DESCRIPTOR *)pUpdateSD)->Control &
~(SE_DACL_PROTECTED | SE_SACL_PROTECTED);
if(!FLAG_ON(SeInfo, DACL_SECURITY_INFORMATION))
{
fProtected |= SE_DACL_PROTECTED;
}
if(!FLAG_ON(SeInfo, SACL_SECURITY_INFORMATION))
{
fProtected |= SE_SACL_PROTECTED;
}
dwErr = PropagateFileRightsDeep(pUpdateSD,
pUpdateParentSD,
SeInfo,
pwszFile,
pwszProperty,
pcProcessed,
pfStopFlag,
fProtected,
hProcessToken,
FailureLogList);
}
//
// If that worked, write out our updated root security descriptor
//
if(dwErr == ERROR_SUCCESS)
{
dwErr = SetFilePropertyRights(hObject,
SeInfo,
pwszProperty,
pUpdateSD );
}
AccFree(pUpdateParentSD);
}
if(pUpdateSD != NULL)
{
DestroyPrivateObjectSecurity(&pUpdateSD);
}
}
else
{
if(dwErr == ERROR_SUCCESS &&
FLAG_ON(SeInfo, OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION))
{
if(hOpenObject == NULL)
{
dwErr = OpenFileObject(pwszFile,
GetDesiredAccess(WRITE_ACCESS_RIGHTS,
SeInfo),
&hObject,
FALSE);
}
else
{
hObject = hOpenObject;
}
if(dwErr == ERROR_SUCCESS)
{
dwErr = SetFilePropertyRights(hObject,
SeInfo,
pwszProperty,
pSD);
}
}
}
if(dwErr == ERROR_SUCCESS)
{
dwErr = WritePropagationFailureList(MARTAEVT_DIRECTORY_PROPAGATION_FAILED,
FailureLogList,
hProcessToken);
//
// Temporary hack. The failure list should be written to the eventlog rather
// than to a registry key.
//
// Remember to change this in future.
// KedarD
//
dwErr = ERROR_SUCCESS;
}
FileCleanup:
if(hObject != hOpenObject)
{
NtClose(hObject);
}
AccFree(pReadSD);
acDebugOut((DEB_TRACE,
"Out SetAndPropagateFilePropertyRights: %ld\n",
dwErr));
return(dwErr);
}
//+---------------------------------------------------------------------------
//
// Function: SetAndPropagateFilePropertyRightsByHandle
//
// Synopsis: Same as above, but deals with a handle to the open object
// as opposed to a file name.
//
// Arguments: [IN hObject] -- File handle
// [IN pwszProperty] -- Property to set it on
// [IN RootAccList] -- CAccessList that indicates
// what access is to be set on
// the object
// [IN pfStopFlag] -- Address of the stop flag
// to be monitored
// [IN pcProcessed] -- Count of processed items
//
// Returns: ERROR_SUCCESS -- Success
// ERROR_INVALID_PARAMETER -- A bad paramter was given
//
//----------------------------------------------------------------------------
DWORD
SetAndPropagateFilePropertyRightsByHandle(IN HANDLE hObject,
IN PWSTR pwszProperty,
IN CAccessList& RootAccList,
IN PULONG pfStopFlag,
IN PULONG pcProcessed)
{
acDebugOut((DEB_TRACE, "in SetAndPropagateFilePropertyRightsByHandle\n"));
CHECK_HEAP
//
// We'll do this the easy way... convert it to a path, and call on up
//
PWSTR pwszPath = NULL;
DWORD dwErr = ConvertFileHandleToName(hObject,
&pwszPath);
if(dwErr == ERROR_SUCCESS)
{
dwErr = SetAndPropagateFilePropertyRights(pwszPath,
pwszProperty,
RootAccList,
pfStopFlag,
pcProcessed,
hObject);
AccFree(pwszPath);
}
acDebugOut((DEB_TRACE,
"Out SetAndPropagateFilePropertyRightsByHandle: %ld\n",
dwErr));
return(dwErr);
}
//+---------------------------------------------------------------------------
//
// Function: GetLMDfsPaths
//
// Synopsis: Given a DFS path, this function will return a list of the
// LANMAN shares supporting this path. If any non-LM paths are
// found, they are ignored.
//
// Arguments: [IN pwszPath] -- Path to check
// [OUT pcItems] -- Where the count of the
// number of items in the list
// is returned
// [OUT pppwszLocalList] -- OPTIONAL. If present, the
// list of LM paths is returned
// here.
//
// Returns: ERROR_SUCCESS -- Success
// ERROR_NOT_ENOUGH_MEMORY -- A memory allocation failed
//
//----------------------------------------------------------------------------
DWORD
GetLMDfsPaths(IN PWSTR pwszPath,
OUT PULONG pcItems,
OUT PWSTR **pppwszLocalList OPTIONAL)
{
DWORD dwErr = ERROR_SUCCESS;
CHECK_HEAP
PDFS_INFO_3 pDI3;
dwErr = LoadDLLFuncTable();
if(dwErr != ERROR_SUCCESS)
{
return(dwErr);
}
dwErr = (*DLLFuncs.PNetDfsGetInfo)(pwszPath,
NULL,
NULL,
3,
(PBYTE *)&pDI3);
//
// Now, build the list of information to return.
//
if(dwErr == ERROR_SUCCESS)
{
//
// Go through and size our list
//
*pcItems = 0;
DWORD dwSize = sizeof(PWSTR) * pDI3->NumberOfStorages;
for(ULONG iIndex = 0; iIndex < pDI3->NumberOfStorages; iIndex++)
{
WCHAR wszUNCPath[MAX_PATH + 1];
swprintf(wszUNCPath,
L"\\\\%ws\\%ws",
pDI3->Storage[iIndex].ServerName,
pDI3->Storage[iIndex].ShareName);
dwErr = IsFilePathLocalOrLM(wszUNCPath);
if(dwErr != ERROR_SUCCESS)
{
if(dwErr == ERROR_PATH_NOT_FOUND)
{
dwErr = ERROR_SUCCESS;
continue;
}
else
{
break;
}
}
(*pcItems)++;
if(pppwszLocalList != NULL)
{
//
// Set a flag in the information so we can look up the
// valid names quicker below, when we copy them
//
pDI3->Storage[iIndex].State = 0xFFFFFFFF;
dwSize += SIZE_PWSTR(pDI3->Storage[iIndex].ServerName);
dwSize += SIZE_PWSTR(pDI3->Storage[iIndex].ShareName);
dwSize += 2 * sizeof(WCHAR); // Room for leading \\'s. The
// NULL of the server name
// gives the seperator
}
}
if(dwErr == ERROR_SUCCESS && pppwszLocalList != NULL)
{
//
// Now, allocate, and we'll fill
//
*pppwszLocalList = (PWSTR *)AccAlloc(dwSize);
if(*pppwszLocalList == NULL)
{
dwErr = ERROR_NOT_ENOUGH_MEMORY;
}
else
{
PWSTR pwszStrStart = (PWSTR)(*pppwszLocalList +
(sizeof(PWSTR) * pDI3->NumberOfStorages));
for(iIndex = 0; iIndex < pDI3->NumberOfStorages; iIndex++)
{
if(pDI3->Storage[iIndex].State == 0xFFFFFFFF)
{
(*pppwszLocalList)[iIndex] = pwszStrStart;
swprintf(pwszStrStart,
L"\\\\%ws\\%ws",
pDI3->Storage[iIndex].ServerName,
pDI3->Storage[iIndex].ShareName);
pwszStrStart += wcslen(pwszStrStart) + 1;
}
}
}
}
//
// Make sure to free our buffer
//
(*DLLFuncs.PNetApiBufferFree)(pDI3);
}
return(dwErr);
}
//+---------------------------------------------------------------------------
//
// Function: PropagateFileRightsDeep, 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 file systems, 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 pParentSD] -- The current parent sd
// [IN pOldParentSD] -- The previous parent SD (before
// the current parent SD was
// stamped on the object)
// [IN SeInfo] -- What is being written
// [IN pwszFile] -- Parent file path
// [IN pwszProperty] -- What property is being
// written
// [IN pcProcessed] -- Where the number processed is
// returned.
// [IN pfStopFlag] -- Stop flag to monitor
// [IN fProtectedFlag] -- Indicates whether acls are protected or not
// [IN hToken] -- Handle to the process token
//
// Returns: ERROR_SUCCESS -- Success
// ERROR_NOT_ENOUGH_MEMORY -- A memory allocation failed
//
//----------------------------------------------------------------------------
#define ALL_STR L"\\*.*"
DWORD
PropagateFileRightsDeep(IN PSECURITY_DESCRIPTOR pParentSD,
IN PSECURITY_DESCRIPTOR pOldParentSD,
IN SECURITY_INFORMATION SeInfo,
IN PWSTR pwszFile,
IN PWSTR pwszProperty,
IN PULONG pcProcessed,
IN PULONG pfStopFlag,
IN ULONG fProtectedFlag,
IN HANDLE hToken,
IN OUT CSList& LogList)
{
acDebugOut((DEB_TRACE, "in PropagateFileRightsDeep\n"));
DWORD dwErr = ERROR_SUCCESS;
WIN32_FIND_DATA FindData;
HANDLE hFind = NULL;
HANDLE hChild = NULL;
ULONG cRootLen = SIZE_PWSTR(pwszFile), Protected = 0;
SECURITY_DESCRIPTOR *pChildSD = NULL;
PSECURITY_DESCRIPTOR pNewSD = NULL;
PWSTR pwszFull = NULL;
BOOL fUpdateChild = FALSE; // Write out the child?
BOOL fAccFreeChild = TRUE; // How to free the child
BOOL fNoPropagate = FALSE;
BOOL fLoggedFailure = FALSE;
acDebugOut((DEB_TRACE_PROP, " Path: %ws\n", pwszFile));
acDebugOut((DEB_TRACE_PROP, " ParentSD: 0x%lx, OldParentSD: 0x%lx\n",
pParentSD, pOldParentSD));
//
// If the any part of the node is going be ingnored because of protection, log it
//
if(FLAG_ON(SeInfo, DACL_SECURITY_INFORMATION) && FLAG_ON(fProtectedFlag, SE_DACL_PROTECTED))
{
Protected |= SE_DACL_PROTECTED;
}
if(FLAG_ON(SeInfo, SACL_SECURITY_INFORMATION) && FLAG_ON(fProtectedFlag, SE_SACL_PROTECTED))
{
Protected |= SE_SACL_PROTECTED;
}
if( Protected != 0)
{
dwErr = InsertPropagationFailureEntry(LogList,
0,
Protected,
pwszFile);
if(dwErr != ERROR_SUCCESS)
{
return(dwErr);
}
}
//
// Check to see if we've reached full protection saturation
//
if(fProtectedFlag == (SE_DACL_PROTECTED | SE_SACL_PROTECTED))
{
acDebugOut((DEB_TRACE_PROP, "Parent of %ws is fully or effectively protected\n",
pwszFile));
return(ERROR_SUCCESS);
}
//
// Build the full path name
//
PWSTR pwszFindRoot = (PWSTR)AccAlloc(cRootLen + sizeof(ALL_STR));
if(pwszFindRoot == NULL)
{
dwErr = ERROR_NOT_ENOUGH_MEMORY;
}
else
{
swprintf(pwszFindRoot,
L"%ws%ws",
pwszFile,
ALL_STR);
hFind = FindFirstFile(pwszFindRoot,
&FindData);
if(hFind == INVALID_HANDLE_VALUE)
{
dwErr = InsertPropagationFailureEntry(LogList,
GetLastError(),
0,
pwszFile);
fNoPropagate = TRUE;
}
}
//
// Start processing all the files
//
while(dwErr == ERROR_SUCCESS && fNoPropagate == FALSE)
{
//
// Ignore the . and ..
//
if(_wcsicmp(FindData.cFileName, L".") != 0 &&
wcscmp(FindData.cFileName, L"..") != 0)
{
//
// Now, build the full path...
//
pwszFull = (PWSTR)AccAlloc(cRootLen + SIZE_PWSTR(FindData.cFileName));
if(pwszFull == NULL)
{
dwErr = ERROR_NOT_ENOUGH_MEMORY;
break;
}
wcscpy(pwszFull,
pwszFile);
if(pwszFull[(cRootLen / sizeof(WCHAR)) - 2] != L'\\')
{
wcscat(pwszFull,
L"\\");
}
wcscat(pwszFull,
FindData.cFileName);
acDebugOut((DEB_TRACE_PROP,
"Processing %ws\n",
pwszFull));
//
// Open the object
//
if(hChild != NULL)
{
NtClose(hChild);
hChild = NULL;
}
dwErr = OpenFileObject(pwszFull,
GetDesiredAccess(MODIFY_ACCESS_RIGHTS,
SeInfo) | FILE_READ_ATTRIBUTES | READ_CONTROL,
&hChild,
FALSE);
if(dwErr == ERROR_SUCCESS)
{
//
// Ok, is it a file or directory
//
BOOL fIsCont;
dwErr = IsFileContainer(hChild,
&fIsCont);
//
// First, we have to read the current security descriptor
// on the object
//
if(dwErr == ERROR_SUCCESS)
{
//
// Get the owner and the group if we have to
//
if(pChildSD == NULL ||
!FLAG_ON(SeInfo, OWNER_SECURITY_INFORMATION) ||
!FLAG_ON(SeInfo, GROUP_SECURITY_INFORMATION))
{
AccFree(pChildSD);
pChildSD = NULL;
dwErr = ReadFileSD(hChild,
SeInfo |
OWNER_SECURITY_INFORMATION |
GROUP_SECURITY_INFORMATION,
0,
(PSECURITY_DESCRIPTOR *)&pChildSD);
}
if(dwErr == ERROR_SUCCESS)
{
//
// If we don't have an uplevel SecurityDescriptor,
// we'll need to update it with our old parent sd
//
if(!FLAG_ON(pChildSD->Control,
SE_DACL_AUTO_INHERITED |
SE_SACL_AUTO_INHERITED))
{
dwErr = ConvertToAutoInheritSD(pOldParentSD,
pChildSD,
fIsCont,
&gFileGenMap,
&pNewSD);
if(dwErr == ERROR_SUCCESS)
{
if(fAccFreeChild == TRUE)
{
AccFree(pChildSD);
}
else
{
DestroyPrivateObjectSecurity((PSECURITY_DESCRIPTOR *)&pChildSD);
}
pChildSD = (SECURITY_DESCRIPTOR *)pNewSD;
fAccFreeChild = FALSE;
pNewSD = NULL;
}
}
}
//
// Now, compute the new security descriptor
//
if(dwErr == ERROR_SUCCESS)
{
DebugDumpSD("CPOS ParentSD", pParentSD);
DebugDumpSD("CPOS ChildSD", pChildSD);
if(CreatePrivateObjectSecurityEx(
pParentSD,
pChildSD,
&pNewSD,
NULL,
fIsCont,
SEF_DACL_AUTO_INHERIT |
SEF_SACL_AUTO_INHERIT |
SEF_AVOID_OWNER_CHECK |
SEF_AVOID_PRIVILEGE_CHECK,
hToken,
&gFileGenMap) == FALSE)
{
dwErr = GetLastError();
}
#ifdef DBG
if(dwErr == ERROR_SUCCESS)
{
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(fProtectedFlag == (SE_DACL_PROTECTED | SE_SACL_PROTECTED))
{
fIsCont = FALSE;
}
//
// If we haven't changed the acl, security descriptor, then
// we can also quit
//
if(EqualSecurityDescriptors(pNewSD, pChildSD))
{
fIsCont = FALSE;
}
}
}
//
// Now, if it's a directory, call ourselves
//
if(dwErr == ERROR_SUCCESS && fIsCont == TRUE)
{
dwErr = PropagateFileRightsDeep(pNewSD,
pChildSD,
SeInfo,
pwszFull,
pwszProperty,
pcProcessed,
pfStopFlag,
fProtectedFlag,
hToken,
LogList);
}
//
// Free the old child, since we won't need it anymore
//
if(fAccFreeChild == TRUE)
{
AccFree(pChildSD);
}
else
{
DestroyPrivateObjectSecurity((PSECURITY_DESCRIPTOR *)
&pChildSD);
}
pChildSD = NULL;
}
}
else
{
dwErr = InsertPropagationFailureEntry(LogList,
dwErr,
0,
pwszFull);
fLoggedFailure = TRUE;
}
acDebugOut((DEB_TRACE_PROP,
"Processed %ws: %lu\n",
pwszFull,
dwErr));
//
// Finally, set the new security
//
if(dwErr == ERROR_SUCCESS && fLoggedFailure == FALSE)
{
//
// Now, we'll simply stamp it on the object
//
dwErr = SetFilePropertyRights(hChild,
SeInfo,
pwszProperty,
pNewSD);
(*pcProcessed)++;
}
DestroyPrivateObjectSecurity(&pNewSD);
pNewSD = NULL;
AccFree(pwszFull);
pwszFull = NULL;
fLoggedFailure = FALSE;
}
CLEANUP_ON_INTERRUPT(pfStopFlag);
if(dwErr != ERROR_SUCCESS)
{
break;
}
if(FindNextFile(hFind,
&FindData) == FALSE)
{
dwErr = GetLastError();
}
}
if(dwErr == ERROR_NO_MORE_FILES)
{
dwErr = ERROR_SUCCESS;
}
FileCleanup:
if(hChild != NULL)
{
NtClose(hChild);
}
if(hFind != NULL)
{
FindClose(hFind);
}
AccFree(pwszFull);
AccFree(pwszFindRoot);
if(pNewSD != NULL)
{
DestroyPrivateObjectSecurity(&pNewSD);
}
acDebugOut((DEB_TRACE, "Out PropagateFileRightsDeep: %ld\n", dwErr));
return(dwErr);
}
//+---------------------------------------------------------------------------
//
// Function: MakeSDSelfRelative
//
// Synopsis: Makes the indicated security descriptor self relative,
// if it isn't already.
//
// Arguments: [IN pOldSD] -- The security descriptor to
// convert
// [OUT ppNewSD] -- Where the new SD is returned
// [OUT ppDAcl] -- If non-NULL, the DACL pointer
// is returned here
// [OUT ppSAcl] -- If non-NULL, the SACL pointer
// is returned here
// [IN fFreeOldSD] -- If true, AccFree is called
// on the old SD.
// [IN fRtlAlloc] -- If true, use the RtlAllocation
// routines instead of AccAlloc
//
// Returns: ERROR_SUCCESS -- Success
// ERROR_NOT_ENOUGH_MEMORY -- A memory allocation failed
//
//----------------------------------------------------------------------------
DWORD
MakeSDSelfRelative(IN PSECURITY_DESCRIPTOR pOldSD,
OUT PSECURITY_DESCRIPTOR *ppNewSD,
OUT PACL *ppDAcl,
OUT PACL *ppSAcl,
IN BOOL fFreeOldSD,
IN BOOL fRtlAlloc)
{
DWORD dwErr = ERROR_SUCCESS;
CHECK_HEAP
if(pOldSD == NULL)
{
*ppNewSD = NULL;
return(dwErr);
}
//
// If it's already self relative and we don't need it to be allocated via
// RtlAllocateHead, simply return what we have
//
if(FLAG_ON(((SECURITY_DESCRIPTOR *)pOldSD)->Control,
SE_SELF_RELATIVE) && fRtlAlloc == FALSE)
{
*ppNewSD = pOldSD;
}
else
{
DWORD dwSize = RtlLengthSecurityDescriptor(pOldSD);
if(fRtlAlloc == FALSE)
{
*ppNewSD = (PSECURITY_DESCRIPTOR)AccAlloc(dwSize);
}
else
{
*ppNewSD = (PSECURITY_DESCRIPTOR)RtlAllocateHeap(RtlProcessHeap(),
0, dwSize);
}
if(*ppNewSD == NULL)
{
dwErr = ERROR_NOT_ENOUGH_MEMORY;
}
else
{
if(FLAG_ON(((SECURITY_DESCRIPTOR *)pOldSD)->Control, SE_SELF_RELATIVE))
{
RtlCopyMemory(*ppNewSD, pOldSD, dwSize);
}
else
{
if(MakeSelfRelativeSD(pOldSD,
*ppNewSD,
&dwSize) == FALSE)
{
dwErr = GetLastError();
if(fRtlAlloc == FALSE)
{
AccFree(*ppNewSD);
*ppNewSD = NULL;
}
else
{
RtlFreeHeap(RtlProcessHeap(), 0, *ppNewSD);
}
}
}
}
}
if(dwErr == ERROR_SUCCESS)
{
if(ppDAcl != NULL)
{
*ppDAcl =
RtlpDaclAddrSecurityDescriptor((SECURITY_DESCRIPTOR *)*ppNewSD);
}
if(ppSAcl != NULL)
{
*ppSAcl =
RtlpSaclAddrSecurityDescriptor((SECURITY_DESCRIPTOR *)*ppNewSD);
}
//
// Don't release it if we're returning it...
//
if(fFreeOldSD == TRUE && *ppNewSD != pOldSD)
{
AccFree(pOldSD);
}
}
return(dwErr);
}
//+---------------------------------------------------------------------------
//
// Function: UpdateFileSDByPath
//
// Synopsis: Determines the inheritance necessary for the current security
// descriptor given the path to the object
//
// Arguments: [IN pCurrentSD] -- The security descriptor to
// update
// [IN pwszPath] -- The path to th object
// [IN hFile] -- Handle to the open file
// [IN SeInfo] -- The security information of
// the current SD.
// [IN fIsContainer] -- Does the Sec. Desc. refer to
// a container?
// [OUT ppNewSD] -- Where the new SD is returned
//
// Returns: ERROR_SUCCESS -- Success
//
// Notes: The returned security descriptor must be freed via a call to
// DestroyPrivateObjectSecurity
//
//----------------------------------------------------------------------------
DWORD
UpdateFileSDByPath(IN PSECURITY_DESCRIPTOR pCurrentSD,
IN PWSTR pwszPath,
IN HANDLE hFile,
IN HANDLE hProcessToken,
IN SECURITY_INFORMATION SeInfo,
IN BOOL fIsContainer,
OUT PSECURITY_DESCRIPTOR *ppNewSD)
{
DWORD dwErr = ERROR_SUCCESS;
CHECK_HEAP
acDebugOut((DEB_TRACE, "In UpdateFileSDByPath\n"));
PSECURITY_DESCRIPTOR pSD = pCurrentSD;
PSECURITY_DESCRIPTOR pParentSD = NULL;
if(!FLAG_ON(SeInfo, OWNER_SECURITY_INFORMATION) ||
!FLAG_ON(SeInfo, GROUP_SECURITY_INFORMATION))
{
//
// We'll have to reopen by path to get this to work properly, since we're
// now reading the owner/group
//
HANDLE hLocalFile;
dwErr = OpenFileObject(pwszPath,
GetDesiredAccess(READ_ACCESS_RIGHTS,
SeInfo |
OWNER_SECURITY_INFORMATION |
GROUP_SECURITY_INFORMATION) |
FILE_READ_ATTRIBUTES,
&hLocalFile,
FALSE);
//
// If we get back an access denied from the open request, try it with the
// handle we were given on input
//
if(dwErr == ERROR_ACCESS_DENIED)
{
hLocalFile = hFile;
dwErr = ERROR_SUCCESS;
}
if(dwErr == ERROR_SUCCESS)
{
dwErr = GetKernelSecurityInfo(hLocalFile,
SeInfo |
OWNER_SECURITY_INFORMATION |
GROUP_SECURITY_INFORMATION,
NULL,
NULL,
&pSD);
if(hLocalFile != hFile)
{
NtClose(hLocalFile);
}
}
}
if(dwErr == ERROR_SUCCESS)
{
//
// Get the parent security descriptor
//
ACTRL_RIGHTS_INFO RightsInfo;
RightsInfo.SeInfo = SeInfo;
RightsInfo.pwszProperty = 0;
dwErr = GetFileParentRights(pwszPath,
&RightsInfo,
1,
NULL,
NULL,
&pParentSD);
}
//
// Finally, do the update
//
if(dwErr == ERROR_SUCCESS)
{
acDebugOut((DEB_TRACE_PROP,"Update being called: Parent info: 0x%lx\n",
pParentSD));
acDebugOut((DEB_TRACE_PROP,"Child: path %ws, 0x%lx\n",
pwszPath, pSD));
//
// Turn off impersonation here
//
HANDLE hThreadToken;
if (OpenThreadToken(
GetCurrentThread(),
MAXIMUM_ALLOWED,
TRUE, // OpenAsSelf
&hThreadToken
)) {
//
// We're impersonating, turn it off and remember the handle
//
RevertToSelf();
} else {
hThreadToken = NULL;
}
if(CreatePrivateObjectSecurityEx(pParentSD,
pSD,
ppNewSD,
NULL,
fIsContainer,
SEF_DACL_AUTO_INHERIT |
SEF_SACL_AUTO_INHERIT |
SEF_AVOID_OWNER_CHECK |
SEF_AVOID_PRIVILEGE_CHECK,
hProcessToken,
&gFileGenMap) == FALSE)
{
dwErr = GetLastError();
}
if (hThreadToken != NULL)
{
(VOID) SetThreadToken (
NULL,
hThreadToken
);
CloseHandle( hThreadToken );
hThreadToken = NULL;
}
AccFree(pParentSD);
}
//
// See if we had to read a new security descriptor for the object. If so,
// we'll need to release that memory as well
//
if(pSD != pCurrentSD)
{
AccFree(pSD);
}
acDebugOut((DEB_TRACE, "Out UpdateFileSDByPath: %ld\n", dwErr));
return(dwErr);
}