|
|
//+---------------------------------------------------------------------------
//
// 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_U RelativeName; IO_STATUS_BLOCK IoStatusBlock; PVOID FreeBuffer = NULL; BOOL ReleaseRelativeName = FALSE;
//
// 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(RelativeName));
} else {
if(RtlDosPathNameToRelativeNtPathName_U(pObjectName, &FileName, NULL, &RelativeName)) {
ReleaseRelativeName = TRUE; FreeBuffer = FileName.Buffer;
if ( RelativeName.RelativeName.Length ) { FileName = 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); }
if (ReleaseRelativeName) { RtlReleaseRelativeName(&RelativeName); } 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));
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(); }
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); }
|