/*++ File Description: This file contains the utility function used to go scan drives and examine the related ACLs. Author: Matt Holle (matth) Feb 1998 --*/ // // System header files // #include // // Disable the DbgPrint for non-debug builds // #ifndef DBG #define _DBGNT_ #endif #include #include #include #include // // CRT header files // #include // // Private header files // #include "setupcl.h" NTSTATUS DeleteUsnJournal( PWSTR DrivePath ); NTSTATUS ResetACLs( IN WCHAR *DirName, ULONG indent ); NTSTATUS EnumerateDrives( VOID ) /*++ =============================================================================== Routine Description: This function will enumerate all drives on the machine. We're looking for NTFS volumes. For each that we find, we'll go scan the drive and poke each directory and file for its ACLs. Arguments: Return Value: Status is returned. =============================================================================== --*/ { NTSTATUS Status = STATUS_SUCCESS; OBJECT_ATTRIBUTES ObjectAttributes; HANDLE DosDevicesDir; CHAR DirInfoBuffer[2048], LinkTargetBuffer[2048]; UNICODE_STRING UnicodeString, LinkTarget, DesiredPrefix1, DesiredPrefix2, LinkTypeName; POBJECT_DIRECTORY_INFORMATION DirInfo; ULONG Context, Length; HANDLE Handle; BOOLEAN b; // // Open \DosDevices // RtlInitUnicodeString(&UnicodeString,L"\\DosDevices"); InitializeObjectAttributes( &ObjectAttributes, &UnicodeString, OBJ_CASE_INSENSITIVE | OBJ_OPENIF | OBJ_PERMANENT, NULL, NULL ); Status = NtOpenDirectoryObject(&DosDevicesDir,DIRECTORY_QUERY,&ObjectAttributes); TEST_STATUS_RETURN( "SETUPCL: EnumerateDrives - Failed to open DosDevices." ); LinkTarget.Buffer = (PVOID)LinkTargetBuffer; RtlInitUnicodeString(&LinkTypeName,L"SymbolicLink"); RtlInitUnicodeString(&DesiredPrefix1,L"\\Device\\Harddisk"); RtlInitUnicodeString(&DesiredPrefix2,L"\\Device\\Volume"); DirInfo = (POBJECT_DIRECTORY_INFORMATION)DirInfoBuffer; b = TRUE; // // Query first object in \DosDevices directory // Status = NtQueryDirectoryObject( DosDevicesDir, DirInfo, sizeof(DirInfoBuffer), TRUE, TRUE, &Context, &Length ); while(NT_SUCCESS(Status)) { // // Terminate these guys just in case... // DirInfo->Name.Buffer[DirInfo->Name.Length/sizeof(WCHAR)] = 0; DirInfo->TypeName.Buffer[DirInfo->TypeName.Length/sizeof(WCHAR)] = 0; DbgPrint( "SETUPCL: EnumerateDrives - About to examine an object: %ws\n", DirInfo->Name.Buffer ); // // Make sure he's a symbolic link. // // It's possible for two objects to link to the same device. To // preclude following duplicate links, disallow any objects except // those that are a drive letter. Crude but effective... // if( (DirInfo->Name.Buffer[1] == L':') && (RtlEqualUnicodeString(&LinkTypeName,&DirInfo->TypeName,TRUE)) ) { DbgPrint( "\tSETUPCL: EnumerateDrives - Object: %ws is a symbolic link\n", DirInfo->Name.Buffer ); InitializeObjectAttributes( &ObjectAttributes, &DirInfo->Name, OBJ_CASE_INSENSITIVE, DosDevicesDir, NULL ); Status = NtOpenSymbolicLinkObject( &Handle, SYMBOLIC_LINK_ALL_ACCESS, &ObjectAttributes ); if(NT_SUCCESS(Status)) { LinkTarget.Length = 0; LinkTarget.MaximumLength = sizeof(LinkTargetBuffer); Status = NtQuerySymbolicLinkObject( Handle, &LinkTarget, NULL ); LinkTarget.Buffer[LinkTarget.Length/sizeof(WCHAR)] = 0; DbgPrint( "\tSETUPCL: EnumerateDrives - We queried him and his name is %ws.\n", LinkTarget.Buffer ); NtClose(Handle); if( NT_SUCCESS(Status) && ( RtlPrefixUnicodeString(&DesiredPrefix1,&LinkTarget,TRUE) || RtlPrefixUnicodeString(&DesiredPrefix2,&LinkTarget,TRUE) ) ) { IO_STATUS_BLOCK IoStatusBlock; UCHAR buffer[4096]; PFILE_FS_ATTRIBUTE_INFORMATION Info = (PFILE_FS_ATTRIBUTE_INFORMATION)buffer; OBJECT_ATTRIBUTES Obja; // // OK, this is a symbolic link to a hard drive. // Make sure it's 0-terminated. // LinkTarget.Buffer[LinkTarget.Length/sizeof(WCHAR)] = 0; DbgPrint( "\tSETUPCL: EnumerateDrives - He's a drive.\n" ); // // Is he an NTFS drive? Open him and see. // InitializeObjectAttributes( &Obja, &LinkTarget, OBJ_CASE_INSENSITIVE, NULL, NULL ); Status = NtOpenFile( &Handle, SYNCHRONIZE | FILE_READ_DATA | FILE_WRITE_DATA, &Obja, &IoStatusBlock, FILE_SHARE_READ | FILE_SHARE_WRITE, FILE_SYNCHRONOUS_IO_ALERT); if( NT_SUCCESS(Status) ) { Status = NtQueryVolumeInformationFile( Handle, &IoStatusBlock, buffer, sizeof(buffer), FileFsAttributeInformation ); if( NT_SUCCESS(Status) ) { Info->FileSystemName[Info->FileSystemNameLength/sizeof(WCHAR)] = 0; DbgPrint( "\tSETUPCL: EnumerateDrives - His file system is: %ws\n", Info->FileSystemName ); if( !_wcsicmp(Info->FileSystemName,L"NTFS") ) { // // He's NTFS. Go whack the change journal, then // scan this drive and fix up the ACLs. // DeleteUsnJournal( LinkTarget.Buffer ); // // ISSUE-2002/02/26-brucegr,jcohen - potential buffer overrun? // wcscat( LinkTarget.Buffer, L"\\" ); ResetACLs( LinkTarget.Buffer, 0 ); } } else { TEST_STATUS( "SETUPCL: EnumerateDrives - failed call to NtQueryVolumeInformationFile" ); } } else { TEST_STATUS( "SETUPCL: EnumerateDrives - Failed NtOpenFile on this drive" ); } NtClose(Handle); } } } // // Query next object in \DosDevices directory // Status = NtQueryDirectoryObject( DosDevicesDir, DirInfo, sizeof(DirInfoBuffer), TRUE, FALSE, &Context, &Length ); } NtClose(DosDevicesDir); return( STATUS_SUCCESS ); } NTSTATUS ResetACLs( IN WCHAR *ObjectName, ULONG indent ) /*++ =============================================================================== Routine Description: This function will go search a drive and inspect each file and directory for an ACL. If found, it will look for, and replace, any ACL that contains the old SID with the new SID. Arguments: Return Value: Status is returned. =============================================================================== --*/ { NTSTATUS Status = STATUS_SUCCESS; UNICODE_STRING UnicodeString; OBJECT_ATTRIBUTES Obja; IO_STATUS_BLOCK IoStatusBlock; HANDLE Handle; PFILE_BOTH_DIR_INFORMATION DirectoryInfo; PWSTR NewObjectName; DWORD dwDirectoryInfoSize; BOOLEAN bStartScan = TRUE, bContinue = TRUE; ULONG i; #if 0 for( i = 0; i < indent; i++ ) DbgPrint( " " ); DbgPrint( "About to operate on a new object: %ws\n", ObjectName ); #endif DisplayUI(); // // Open the file/directory and whack his ACL. // INIT_OBJA(&Obja, &UnicodeString, ObjectName); Status = NtOpenFile( &Handle, READ_CONTROL | WRITE_DAC | WRITE_OWNER | ACCESS_SYSTEM_SECURITY, &Obja, &IoStatusBlock, FILE_SHARE_READ | FILE_SHARE_WRITE, 0 ); TEST_STATUS( "SETUPCL: ResetACLs - Failed to open file/directory." ); Status = TestSetSecurityObject( Handle ); TEST_STATUS( "SETUPCL: ResetACLs - Failed to reset ACL on file/directory." ); NtClose( Handle ); // // Now list the directory. // Status = NtOpenFile( &Handle, FILE_LIST_DIRECTORY | SYNCHRONIZE, &Obja, &IoStatusBlock, FILE_SHARE_READ | FILE_SHARE_WRITE, FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT | FILE_OPEN_FOR_BACKUP_INTENT ); // // Don't report this error because we'll fail if the handle points // to a file, which is quite possible and valid. Just quietly return. // // TEST_STATUS_RETURN( "SETUPCL: ResetACLs - Failed to open file/directory for list access." ); if( !NT_SUCCESS(Status) ) { return( STATUS_SUCCESS ); } // // It is gruesome to have the allocation/deallocation of this inside // the while loop, but it saves a *lot* of stack space. We aren't after // speed here. // dwDirectoryInfoSize = (MAX_PATH * 2) + sizeof(FILE_BOTH_DIR_INFORMATION); DirectoryInfo = (PFILE_BOTH_DIR_INFORMATION)RtlAllocateHeap( RtlProcessHeap(), 0, dwDirectoryInfoSize ); if ( NULL == DirectoryInfo ) { bContinue = FALSE; } while( bContinue ) { Status = NtQueryDirectoryFile( Handle, NULL, NULL, NULL, &IoStatusBlock, DirectoryInfo, dwDirectoryInfoSize, FileBothDirectoryInformation, TRUE, NULL, bStartScan ); if ( NT_SUCCESS( Status ) ) { // // Make sure the scan doesn't get restarted... // bStartScan = FALSE; // // Terminate the name, just in case. // DirectoryInfo->FileName[DirectoryInfo->FileNameLength/sizeof(WCHAR)] = 0; } else { if ( STATUS_NO_MORE_FILES == Status ) { Status = STATUS_SUCCESS; } else { PRINT_STATUS( "SETUPCL: ResetACLs - Failed to query directory." ); } // // We want to exit the loop... // bContinue = FALSE; } // // We can't really do anything with encrypted files... // if ( bContinue && ( ( !(DirectoryInfo->FileAttributes & FILE_ATTRIBUTE_DIRECTORY) && !(DirectoryInfo->FileAttributes & FILE_ATTRIBUTE_ENCRYPTED) ) || // // Don't recurse into the "." and ".." directories... // ( (DirectoryInfo->FileAttributes & FILE_ATTRIBUTE_DIRECTORY) && ( (wcscmp( DirectoryInfo->FileName, L"." )) && (wcscmp( DirectoryInfo->FileName, L".." )) ) ) ) ) { // // Calculate the maximum buffer size we need to allocate... // We need to factor in 4 things: 1) the current object // 2) the directory name // 3) a possible backslash // 4) the null terminator // DWORD dwObjectLength = wcslen(ObjectName), dwNewObjectLength = dwObjectLength + (DirectoryInfo->FileNameLength / sizeof(WCHAR)) + 2; // // Build the name of the new object. // NewObjectName = (PWSTR)RtlAllocateHeap( RtlProcessHeap(), 0, dwNewObjectLength * sizeof(WCHAR) ); // // Make sure the allocation succeeded... // if ( NewObjectName ) { memset( NewObjectName, 0, dwNewObjectLength * sizeof(WCHAR) ); wcsncpy( NewObjectName, ObjectName, dwNewObjectLength - 1 ); // // If there's not an ending backslash, append one... // Note: we've already accounted for this possible backslash in the buffer allocation... // if ( ObjectName[dwObjectLength - 1] != L'\\' ) { wcscat( NewObjectName, L"\\" ); } // // Append the FileName buffer onto our NewObjectName buffer... // Note: we've already accounted for the filename length in the buffer allocation... // wcscat( NewObjectName, DirectoryInfo->FileName ); // // Call ourselves on the new object. // ResetACLs( NewObjectName, indent + 1 ); RtlFreeHeap( RtlProcessHeap(), 0, NewObjectName ); } else { PRINT_STATUS( "SETUPCL: ResetACLs - Failed to allocate NewObjectName buffer." ); bContinue = FALSE; } } } // // Free the DirectoryInfo pointer... // if ( DirectoryInfo ) { RtlFreeHeap( RtlProcessHeap(), 0, DirectoryInfo ); } NtClose( Handle ); return( Status ); } NTSTATUS DeleteUsnJournal( PWSTR DrivePath ) /*++ =============================================================================== Routine Description: This function will remove the Change Journal on NTFS partitions. Arguments: DriveLetter Supplies the drive letter of the partition we'll be operating on. Return Value: Status is returned. =============================================================================== --*/ { NTSTATUS Status = STATUS_SUCCESS; UNICODE_STRING UnicodeString; OBJECT_ATTRIBUTES ObjectAttributes; HANDLE FileHandle; IO_STATUS_BLOCK IoStatusBlock; PUSN_JOURNAL_DATA OutputBuffer = NULL; PDELETE_USN_JOURNAL_DATA InputBuffer = NULL; ULONG OutputBufferSize, InputBufferSize; // // Build the volume name, then open it. // INIT_OBJA( &ObjectAttributes, &UnicodeString, DrivePath ); Status = NtOpenFile( &FileHandle, SYNCHRONIZE | FILE_READ_DATA | FILE_WRITE_DATA, &ObjectAttributes, &IoStatusBlock, FILE_SHARE_READ | FILE_SHARE_WRITE, FILE_SYNCHRONOUS_IO_ALERT); TEST_STATUS_RETURN( "SETUPCL: DeleteUsnJournal - Failed to open volume." ); // // Allocate buffers for the query and delete operation. // OutputBufferSize = sizeof(USN_JOURNAL_DATA); OutputBuffer = (PUSN_JOURNAL_DATA)RtlAllocateHeap( RtlProcessHeap(), 0, sizeof(USN_JOURNAL_DATA) ); InputBufferSize = sizeof(DELETE_USN_JOURNAL_DATA); InputBuffer = (PDELETE_USN_JOURNAL_DATA)RtlAllocateHeap( RtlProcessHeap(), 0, sizeof(DELETE_USN_JOURNAL_DATA) ); if( !(OutputBuffer && InputBuffer) ) { DbgPrint( "SETUPCL: DeleteUsnJournal - Failed to allocate buffers.\n" ); // // ISSUE-2002/02/26-brucegr,jcohen - Leaks Input or Output buffers and FileHandle! // return( STATUS_UNSUCCESSFUL ); } Status = NtFsControlFile( FileHandle, NULL, NULL, NULL, &IoStatusBlock, FSCTL_QUERY_USN_JOURNAL, NULL, 0, OutputBuffer, OutputBufferSize ); TEST_STATUS( "SETUPCL: DeleteUsnJournal - Failed to query journal." ); if( NT_SUCCESS( Status ) ) { // // Now delete him. // InputBuffer->DeleteFlags = USN_DELETE_FLAG_DELETE; InputBuffer->UsnJournalID = OutputBuffer->UsnJournalID; Status = NtFsControlFile( FileHandle, NULL, NULL, NULL, &IoStatusBlock, FSCTL_DELETE_USN_JOURNAL, InputBuffer, InputBufferSize , NULL, 0 ); TEST_STATUS( "SETUPCL: DeleteUsnJournal - Failed to delete journal." ); } NtClose( FileHandle ); RtlFreeHeap( RtlProcessHeap(), 0, OutputBuffer ); RtlFreeHeap( RtlProcessHeap(), 0, InputBuffer ); return Status; }