//+--------------------------------------------------------------------------- // // Microsoft Windows // Copyright (C) Microsoft Corporation, 1994 - 1996. // // File: file.cxx // // Contents: Local file support functions // // History: 8/94 davemont Created // //---------------------------------------------------------------------------- #include #pragma hdrstop #include #include #include extern "C" { #include #include #include #include } #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); }