From fc37cc4a02ed13d1a73b941a9f80975600fd1b99 Mon Sep 17 00:00:00 2001 From: David Foerster Date: Tue, 10 May 2016 20:20:14 +0200 Subject: Normalize all line terminators --- src/Driver/BuildDriver.cmd | 332 +- src/Driver/DriveFilter.c | 4306 ++++++++++++------------- src/Driver/DriveFilter.h | 180 +- src/Driver/Driver.rc | 202 +- src/Driver/Driver.vcproj | 796 ++--- src/Driver/DumpFilter.c | 496 +-- src/Driver/DumpFilter.h | 50 +- src/Driver/EncryptedIoQueue.c | 2098 ++++++------ src/Driver/EncryptedIoQueue.h | 330 +- src/Driver/Makefile | 2 +- src/Driver/Ntdriver.c | 7126 ++++++++++++++++++++--------------------- src/Driver/Ntdriver.h | 378 +-- src/Driver/Ntvol.c | 1800 +++++------ src/Driver/Ntvol.h | 44 +- src/Driver/Resource.h | 32 +- src/Driver/Sources | 42 +- src/Driver/VolumeFilter.c | 598 ++-- src/Driver/VolumeFilter.h | 46 +- 18 files changed, 9429 insertions(+), 9429 deletions(-) (limited to 'src/Driver') diff --git a/src/Driver/BuildDriver.cmd b/src/Driver/BuildDriver.cmd index 99fe554b..bfeee2db 100644 --- a/src/Driver/BuildDriver.cmd +++ b/src/Driver/BuildDriver.cmd @@ -1,166 +1,166 @@ -:: -:: Derived from source code of TrueCrypt 7.1a, which is -:: Copyright (c) 2008-2012 TrueCrypt Developers Association and which is governed -:: by the TrueCrypt License 3.0. -:: -:: Modifications and additions to the original source code (contained in this file) -:: and all other portions of this file are Copyright (c) 2013-2016 IDRIX -:: and are governed by the Apache License 2.0 the full text of which is -:: contained in the file License.txt included in VeraCrypt binary and source -:: code distribution packages. -:: - -:: Usage: BuildDriver <-build|-rebuild|-clean> <-release|-debug> <-x86|-x64> [dir2] ... - -@echo off -set TC_ARG_CMD=%~1 -shift -set TC_ARG_TYPE=%~1 -shift -set TC_ARG_ARCH=%~1 -shift - - -:: Windows Driver Kit build number - -set TC_WINDDK_BUILD=7600.16385.1 - - -:: Check for spaces in the current directory path - -cd | find " " >NUL: - -if %ERRORLEVEL% == 0 ( - echo BuildDriver.cmd: error: MS Build does not support building of projects stored in a path containing spaces. >&2 - exit /B 1 -) - - -:: Build options - -set TC_C_DEFINES=-D_WIN32 -DTC_WINDOWS_DRIVER -set TC_C_FLAGS=-nologo -I.. -set TC_C_WARNING_LEVEL=-W4 -set TC_C_DISABLED_WARNINGS=-wd4057 -wd4100 -wd4127 -wd4152 -wd4201 -wd4701 -wd4702 -wd4706 -set TC_LIBRARIAN_FLAGS=-nologo -set TC_LINKER_FLAGS=-nologo -set TC_TEST_SIGN=0 - - -:: Windows Driver Kit root - -set TC_WINDDK_ROOT=%SYSTEMDRIVE%\WinDDK\%TC_WINDDK_BUILD% -if exist "%TC_WINDDK_ROOT%\bin\setenv.bat" goto ddk_found - -set TC_WINDDK_ROOT=%WINDDK_ROOT%\%TC_WINDDK_BUILD% -if exist "%TC_WINDDK_ROOT%\bin\setenv.bat" goto ddk_found - -set TC_WINDDK_ROOT=%WINDDK_ROOT% -if exist "%TC_WINDDK_ROOT%\bin\setenv.bat" goto ddk_found - -echo BuildDriver.cmd: error: Windows Driver Development Kit not found in the default directory. Set WINDDK_ROOT environment variable to point to your Windows DDK installation directory. >&2 -exit /B 1 - -:ddk_found - - -:: CPU architecture - -if "%TC_ARG_ARCH%"=="-x64" ( - set TC_BUILD_ARCH=x64 WNET - set TC_BUILD_ARCH_DIR=amd64 - set TC_ARCH=x64 - set TC_ARCH_SUFFIX=-x64 - set TC_C_DISABLED_WARNINGS=%TC_C_DISABLED_WARNINGS% -wd4328 -wd4366 - set TC_LINKER_FLAGS=%TC_LINKER_FLAGS% -LTCG - if defined TC_KERNEL_TEST_CERTIFICATE_NAME set TC_TEST_SIGN=1 -) else ( - set TC_BUILD_ARCH=WXP - set TC_BUILD_ARCH_DIR=i386 - set TC_ARCH=x86 - set TC_ARCH_SUFFIX= -) - - -:: Build type - -if "%TC_ARG_TYPE%"=="-debug" ( - set TC_BUILD_TYPE=chk - set TC_C_DEFINES=%TC_C_DEFINES% -DDEBUG -D_DEBUG - set TC_BUILD_ALT_DIR=_driver_debug - set TC_COPY_DIR="..\Debug" -) else ( - set TC_BUILD_TYPE=fre - set TC_BUILD_ALT_DIR=_driver_release - set TC_COPY_DIR="..\Release" - set TC_TEST_SIGN=0 -) - - -:: WDK environment - -pushd . -call %TC_WINDDK_ROOT%\bin\setenv %TC_WINDDK_ROOT% %TC_BUILD_TYPE% %TC_BUILD_ARCH% no_oacr || exit /B %errorlevel% -popd - - -:: Build - -if "%TC_ARG_CMD%"=="-rebuild" (set TC_BUILD_OPTS=-c -Z) - -pushd . -:build_dirs - - if "%~1"=="" goto done - cd /D "%~1" || exit /B %errorlevel% - - if "%TC_ARG_CMD%"=="-clean" ( - rd /s /q obj%TC_BUILD_ALT_DIR%\%TC_BUILD_ARCH_DIR% 2>NUL: - rd /q obj%TC_BUILD_ALT_DIR% 2>NUL: - ) else ( - - set USER_C_FLAGS=%TC_C_FLAGS% %TC_C_DISABLED_WARNINGS% -FAcs -Fa%~1\obj%TC_BUILD_ALT_DIR%\%TC_BUILD_ARCH_DIR%\ - set MSC_WARNING_LEVEL=%TC_C_WARNING_LEVEL% - set C_DEFINES=%TC_C_DEFINES% - set RCOPTIONS=/I %MFC_INC_PATH% - set LIBRARIAN_FLAGS=%TC_LIBRARIAN_FLAGS% - set LINKER_FLAGS=%TC_LINKER_FLAGS% - set BUILD_ALT_DIR=%TC_BUILD_ALT_DIR% - - build %TC_BUILD_OPTS% -w -nmake /S -nmake /C 2>build_errors.log 1>&2 - - if errorlevel 1 ( - type build_errors.log - type build_errors_asm.log 2>NUL: - exit /B 1 - ) - del /q build_errors.log build_errors_asm.log build%BUILD_ALT_DIR%.* 2>NUL: - ) - - shift - -goto build_dirs -:done -popd - - -if "%TC_ARG_CMD%"=="-clean" exit /B 0 - -md "%TC_COPY_DIR%\Setup Files" >NUL: 2>NUL: -copy /B /Y obj%TC_BUILD_ALT_DIR%\%TC_BUILD_ARCH_DIR%\veracrypt.sys "%TC_COPY_DIR%\Setup Files\veracrypt%TC_ARCH_SUFFIX%.sys" >NUL: - -if errorlevel 1 ( - echo BuildDriver.cmd: error: Cannot copy target. >&2 - exit /B 1 -) - -if %TC_TEST_SIGN% equ 1 ( - signtool sign /s "%TC_KERNEL_TEST_CERTIFICATE_STORE%" /n "%TC_KERNEL_TEST_CERTIFICATE_NAME%" "%TC_COPY_DIR%\Setup Files\veracrypt%TC_ARCH_SUFFIX%.sys" >NUL: - - if errorlevel 1 ( - echo BuildDriver.cmd: error: Cannot test-sign target. >&2 - exit /B 1 - ) -) - -exit /B 0 +:: +:: Derived from source code of TrueCrypt 7.1a, which is +:: Copyright (c) 2008-2012 TrueCrypt Developers Association and which is governed +:: by the TrueCrypt License 3.0. +:: +:: Modifications and additions to the original source code (contained in this file) +:: and all other portions of this file are Copyright (c) 2013-2016 IDRIX +:: and are governed by the Apache License 2.0 the full text of which is +:: contained in the file License.txt included in VeraCrypt binary and source +:: code distribution packages. +:: + +:: Usage: BuildDriver <-build|-rebuild|-clean> <-release|-debug> <-x86|-x64> [dir2] ... + +@echo off +set TC_ARG_CMD=%~1 +shift +set TC_ARG_TYPE=%~1 +shift +set TC_ARG_ARCH=%~1 +shift + + +:: Windows Driver Kit build number + +set TC_WINDDK_BUILD=7600.16385.1 + + +:: Check for spaces in the current directory path + +cd | find " " >NUL: + +if %ERRORLEVEL% == 0 ( + echo BuildDriver.cmd: error: MS Build does not support building of projects stored in a path containing spaces. >&2 + exit /B 1 +) + + +:: Build options + +set TC_C_DEFINES=-D_WIN32 -DTC_WINDOWS_DRIVER +set TC_C_FLAGS=-nologo -I.. +set TC_C_WARNING_LEVEL=-W4 +set TC_C_DISABLED_WARNINGS=-wd4057 -wd4100 -wd4127 -wd4152 -wd4201 -wd4701 -wd4702 -wd4706 +set TC_LIBRARIAN_FLAGS=-nologo +set TC_LINKER_FLAGS=-nologo +set TC_TEST_SIGN=0 + + +:: Windows Driver Kit root + +set TC_WINDDK_ROOT=%SYSTEMDRIVE%\WinDDK\%TC_WINDDK_BUILD% +if exist "%TC_WINDDK_ROOT%\bin\setenv.bat" goto ddk_found + +set TC_WINDDK_ROOT=%WINDDK_ROOT%\%TC_WINDDK_BUILD% +if exist "%TC_WINDDK_ROOT%\bin\setenv.bat" goto ddk_found + +set TC_WINDDK_ROOT=%WINDDK_ROOT% +if exist "%TC_WINDDK_ROOT%\bin\setenv.bat" goto ddk_found + +echo BuildDriver.cmd: error: Windows Driver Development Kit not found in the default directory. Set WINDDK_ROOT environment variable to point to your Windows DDK installation directory. >&2 +exit /B 1 + +:ddk_found + + +:: CPU architecture + +if "%TC_ARG_ARCH%"=="-x64" ( + set TC_BUILD_ARCH=x64 WNET + set TC_BUILD_ARCH_DIR=amd64 + set TC_ARCH=x64 + set TC_ARCH_SUFFIX=-x64 + set TC_C_DISABLED_WARNINGS=%TC_C_DISABLED_WARNINGS% -wd4328 -wd4366 + set TC_LINKER_FLAGS=%TC_LINKER_FLAGS% -LTCG + if defined TC_KERNEL_TEST_CERTIFICATE_NAME set TC_TEST_SIGN=1 +) else ( + set TC_BUILD_ARCH=WXP + set TC_BUILD_ARCH_DIR=i386 + set TC_ARCH=x86 + set TC_ARCH_SUFFIX= +) + + +:: Build type + +if "%TC_ARG_TYPE%"=="-debug" ( + set TC_BUILD_TYPE=chk + set TC_C_DEFINES=%TC_C_DEFINES% -DDEBUG -D_DEBUG + set TC_BUILD_ALT_DIR=_driver_debug + set TC_COPY_DIR="..\Debug" +) else ( + set TC_BUILD_TYPE=fre + set TC_BUILD_ALT_DIR=_driver_release + set TC_COPY_DIR="..\Release" + set TC_TEST_SIGN=0 +) + + +:: WDK environment + +pushd . +call %TC_WINDDK_ROOT%\bin\setenv %TC_WINDDK_ROOT% %TC_BUILD_TYPE% %TC_BUILD_ARCH% no_oacr || exit /B %errorlevel% +popd + + +:: Build + +if "%TC_ARG_CMD%"=="-rebuild" (set TC_BUILD_OPTS=-c -Z) + +pushd . +:build_dirs + + if "%~1"=="" goto done + cd /D "%~1" || exit /B %errorlevel% + + if "%TC_ARG_CMD%"=="-clean" ( + rd /s /q obj%TC_BUILD_ALT_DIR%\%TC_BUILD_ARCH_DIR% 2>NUL: + rd /q obj%TC_BUILD_ALT_DIR% 2>NUL: + ) else ( + + set USER_C_FLAGS=%TC_C_FLAGS% %TC_C_DISABLED_WARNINGS% -FAcs -Fa%~1\obj%TC_BUILD_ALT_DIR%\%TC_BUILD_ARCH_DIR%\ + set MSC_WARNING_LEVEL=%TC_C_WARNING_LEVEL% + set C_DEFINES=%TC_C_DEFINES% + set RCOPTIONS=/I %MFC_INC_PATH% + set LIBRARIAN_FLAGS=%TC_LIBRARIAN_FLAGS% + set LINKER_FLAGS=%TC_LINKER_FLAGS% + set BUILD_ALT_DIR=%TC_BUILD_ALT_DIR% + + build %TC_BUILD_OPTS% -w -nmake /S -nmake /C 2>build_errors.log 1>&2 + + if errorlevel 1 ( + type build_errors.log + type build_errors_asm.log 2>NUL: + exit /B 1 + ) + del /q build_errors.log build_errors_asm.log build%BUILD_ALT_DIR%.* 2>NUL: + ) + + shift + +goto build_dirs +:done +popd + + +if "%TC_ARG_CMD%"=="-clean" exit /B 0 + +md "%TC_COPY_DIR%\Setup Files" >NUL: 2>NUL: +copy /B /Y obj%TC_BUILD_ALT_DIR%\%TC_BUILD_ARCH_DIR%\veracrypt.sys "%TC_COPY_DIR%\Setup Files\veracrypt%TC_ARCH_SUFFIX%.sys" >NUL: + +if errorlevel 1 ( + echo BuildDriver.cmd: error: Cannot copy target. >&2 + exit /B 1 +) + +if %TC_TEST_SIGN% equ 1 ( + signtool sign /s "%TC_KERNEL_TEST_CERTIFICATE_STORE%" /n "%TC_KERNEL_TEST_CERTIFICATE_NAME%" "%TC_COPY_DIR%\Setup Files\veracrypt%TC_ARCH_SUFFIX%.sys" >NUL: + + if errorlevel 1 ( + echo BuildDriver.cmd: error: Cannot test-sign target. >&2 + exit /B 1 + ) +) + +exit /B 0 diff --git a/src/Driver/DriveFilter.c b/src/Driver/DriveFilter.c index a8752a5f..c090ee8c 100644 --- a/src/Driver/DriveFilter.c +++ b/src/Driver/DriveFilter.c @@ -1,2153 +1,2153 @@ -/* - Derived from source code of TrueCrypt 7.1a, which is - Copyright (c) 2008-2012 TrueCrypt Developers Association and which is governed - by the TrueCrypt License 3.0. - - Modifications and additions to the original source code (contained in this file) - and all other portions of this file are Copyright (c) 2013-2016 IDRIX - and are governed by the Apache License 2.0 the full text of which is - contained in the file License.txt included in VeraCrypt binary and source - code distribution packages. -*/ - -#include "TCdefs.h" -#include -#include -#include -#include "Cache.h" -#include "Crc.h" -#include "Crypto.h" -#include "Apidrvr.h" -#include "EncryptedIoQueue.h" -#include "Common/Endian.h" -#include "Ntdriver.h" -#include "Ntvol.h" -#include "Volumes.h" -#include "VolumeFilter.h" -#include "Wipe.h" -#include "DriveFilter.h" -#include "Boot/Windows/BootCommon.h" - -static BOOL DeviceFilterActive = FALSE; - -BOOL BootArgsValid = FALSE; -BootArguments BootArgs; -static uint16 BootLoaderSegment; -static BOOL BootDriveSignatureValid = FALSE; - -static KMUTEX MountMutex; - -static volatile BOOL BootDriveFound = FALSE; -static DriveFilterExtension *BootDriveFilterExtension = NULL; -static LARGE_INTEGER BootDriveLength; -static byte BootLoaderFingerprint[WHIRLPOOL_DIGESTSIZE + SHA512_DIGESTSIZE]; - -static BOOL CrashDumpEnabled = FALSE; -static BOOL HibernationEnabled = FALSE; - -static BOOL LegacyHibernationDriverFilterActive = FALSE; -static byte *HibernationWriteBuffer = NULL; -static MDL *HibernationWriteBufferMdl = NULL; - -static uint32 HibernationPreventionCount = 0; - -static BootEncryptionSetupRequest SetupRequest; -static volatile BOOL SetupInProgress = FALSE; -PKTHREAD EncryptionSetupThread = NULL; -static volatile BOOL EncryptionSetupThreadAbortRequested; -static KSPIN_LOCK SetupStatusSpinLock; -static int64 SetupStatusEncryptedAreaEnd; -static BOOL TransformWaitingForIdle; -static NTSTATUS SetupResult; - -static WipeDecoySystemRequest WipeDecoyRequest; -static volatile BOOL DecoySystemWipeInProgress = FALSE; -static volatile BOOL DecoySystemWipeThreadAbortRequested; -static KSPIN_LOCK DecoySystemWipeStatusSpinLock; -static int64 DecoySystemWipedAreaEnd; -PKTHREAD DecoySystemWipeThread = NULL; -static NTSTATUS DecoySystemWipeResult; - - -NTSTATUS LoadBootArguments () -{ - NTSTATUS status = STATUS_UNSUCCESSFUL; - PHYSICAL_ADDRESS bootArgsAddr; - byte *mappedBootArgs; - uint16 bootLoaderSegment; - - KeInitializeMutex (&MountMutex, 0); - - for (bootLoaderSegment = TC_BOOT_LOADER_SEGMENT; - bootLoaderSegment >= TC_BOOT_LOADER_SEGMENT - 64 * 1024 / 16 && status != STATUS_SUCCESS; - bootLoaderSegment -= 32 * 1024 / 16) - { - bootArgsAddr.QuadPart = (bootLoaderSegment << 4) + TC_BOOT_LOADER_ARGS_OFFSET; - Dump ("Checking BootArguments at 0x%x\n", bootArgsAddr.LowPart); - - mappedBootArgs = MmMapIoSpace (bootArgsAddr, sizeof (BootArguments), MmCached); - if (!mappedBootArgs) - return STATUS_INSUFFICIENT_RESOURCES; - - if (TC_IS_BOOT_ARGUMENTS_SIGNATURE (mappedBootArgs)) - { - BootArguments *bootArguments = (BootArguments *) mappedBootArgs; - Dump ("BootArguments found at 0x%x\n", bootArgsAddr.LowPart); - - DumpMem (mappedBootArgs, sizeof (BootArguments)); - - if (bootArguments->BootLoaderVersion == VERSION_NUM - && bootArguments->BootArgumentsCrc32 != GetCrc32 ((byte *) bootArguments, (int) ((byte *) &bootArguments->BootArgumentsCrc32 - (byte *) bootArguments))) - { - Dump ("BootArguments CRC incorrect\n"); - TC_BUG_CHECK (STATUS_CRC_ERROR); - } - - // Sanity check: for valid boot argument, the password is less than 64 bytes long - if (bootArguments->BootPassword.Length <= MAX_PASSWORD) - { - BootLoaderSegment = bootLoaderSegment; - - BootArgs = *bootArguments; - BootArgsValid = TRUE; - burn (bootArguments, sizeof (*bootArguments)); - - BootDriveSignatureValid = TRUE; - - Dump ("BootLoaderVersion = %x\n", (int) BootArgs.BootLoaderVersion); - Dump ("HeaderSaltCrc32 = %x\n", (int) BootArgs.HeaderSaltCrc32); - Dump ("CryptoInfoOffset = %x\n", (int) BootArgs.CryptoInfoOffset); - Dump ("CryptoInfoLength = %d\n", (int) BootArgs.CryptoInfoLength); - Dump ("HiddenSystemPartitionStart = %I64u\n", BootArgs.HiddenSystemPartitionStart); - Dump ("DecoySystemPartitionStart = %I64u\n", BootArgs.DecoySystemPartitionStart); - Dump ("Flags = %x\n", BootArgs.Flags); - Dump ("BootDriveSignature = %x\n", BootArgs.BootDriveSignature); - Dump ("BootArgumentsCrc32 = %x\n", BootArgs.BootArgumentsCrc32); - - if (CacheBootPassword && BootArgs.BootPassword.Length > 0) - { - int pim = CacheBootPim? (int) (BootArgs.Flags >> 16) : 0; - AddPasswordToCache (&BootArgs.BootPassword, pim); - } - - // clear fingerprint - burn (BootLoaderFingerprint, sizeof (BootLoaderFingerprint)); - - status = STATUS_SUCCESS; - } - } - - MmUnmapIoSpace (mappedBootArgs, sizeof (BootArguments)); - } - - return status; -} - - -NTSTATUS DriveFilterAddDevice (PDRIVER_OBJECT driverObject, PDEVICE_OBJECT pdo) -{ - DriveFilterExtension *Extension; - NTSTATUS status; - PDEVICE_OBJECT filterDeviceObject = NULL; - PDEVICE_OBJECT attachedDeviceObject; - - Dump ("DriveFilterAddDevice pdo=%p\n", pdo); - - attachedDeviceObject = IoGetAttachedDeviceReference (pdo); - status = IoCreateDevice (driverObject, sizeof (DriveFilterExtension), NULL, attachedDeviceObject->DeviceType, 0, FALSE, &filterDeviceObject); - - ObDereferenceObject (attachedDeviceObject); - - if (!NT_SUCCESS (status)) - { - filterDeviceObject = NULL; - goto err; - } - - Extension = (DriveFilterExtension *) filterDeviceObject->DeviceExtension; - memset (Extension, 0, sizeof (DriveFilterExtension)); - - status = IoAttachDeviceToDeviceStackSafe (filterDeviceObject, pdo, &(Extension->LowerDeviceObject)); - if (!NT_SUCCESS (status)) - { - goto err; - } - - if (!Extension->LowerDeviceObject) - { - status = STATUS_DEVICE_REMOVED; - goto err; - } - - Extension->IsDriveFilterDevice = Extension->Queue.IsFilterDevice = TRUE; - Extension->DeviceObject = Extension->Queue.DeviceObject = filterDeviceObject; - Extension->Pdo = pdo; - - Extension->Queue.LowerDeviceObject = Extension->LowerDeviceObject; - IoInitializeRemoveLock (&Extension->Queue.RemoveLock, 'LRCV', 0, 0); - - Extension->ConfiguredEncryptedAreaStart = -1; - Extension->ConfiguredEncryptedAreaEnd = -1; - Extension->Queue.EncryptedAreaStart = -1; - Extension->Queue.EncryptedAreaEnd = -1; - Extension->Queue.EncryptedAreaEndUpdatePending = FALSE; - - filterDeviceObject->Flags |= Extension->LowerDeviceObject->Flags & (DO_DIRECT_IO | DO_BUFFERED_IO | DO_POWER_PAGABLE); - filterDeviceObject->Flags &= ~DO_DEVICE_INITIALIZING; - - DeviceFilterActive = TRUE; - return status; - -err: - if (filterDeviceObject) - { - if (Extension->LowerDeviceObject) - IoDetachDevice (Extension->LowerDeviceObject); - - IoDeleteDevice (filterDeviceObject); - } - - return status; -} - - -static void DismountDrive (DriveFilterExtension *Extension, BOOL stopIoQueue) -{ - Dump ("Dismounting drive\n"); - ASSERT (Extension->DriveMounted); - - if (stopIoQueue && EncryptedIoQueueIsRunning (&Extension->Queue)) - EncryptedIoQueueStop (&Extension->Queue); - - crypto_close (Extension->Queue.CryptoInfo); - Extension->Queue.CryptoInfo = NULL; - - crypto_close (Extension->HeaderCryptoInfo); - Extension->HeaderCryptoInfo = NULL; - - Extension->DriveMounted = FALSE; -} - -static void ComputeBootLoaderFingerprint(PDEVICE_OBJECT LowerDeviceObject, byte* ioBuffer /* ioBuffer must be at least 512 bytes long */) -{ - NTSTATUS status; - LARGE_INTEGER offset; - WHIRLPOOL_CTX whirlpool; - sha512_ctx sha2; - ULONG bytesToRead, remainingBytes, bootloaderTotalSize = TC_BOOT_LOADER_AREA_SIZE - TC_BOOT_ENCRYPTION_VOLUME_HEADER_SIZE; - - // clear fingerprint - burn (BootLoaderFingerprint, sizeof (BootLoaderFingerprint)); - - // compute Whirlpool+SHA512 fingerprint of bootloader including MBR - // we skip user configuration fields: - // TC_BOOT_SECTOR_PIM_VALUE_OFFSET = 400 - // TC_BOOT_SECTOR_OUTER_VOLUME_BAK_HEADER_CRC_OFFSET = 402 - // => TC_BOOT_SECTOR_OUTER_VOLUME_BAK_HEADER_CRC_SIZE = 4 - // TC_BOOT_SECTOR_USER_MESSAGE_OFFSET = 406 - // => TC_BOOT_SECTOR_USER_MESSAGE_MAX_LENGTH = 24 - // TC_BOOT_SECTOR_USER_CONFIG_OFFSET = 438 - // - // we have: TC_BOOT_SECTOR_USER_MESSAGE_OFFSET = TC_BOOT_SECTOR_OUTER_VOLUME_BAK_HEADER_CRC_OFFSET + TC_BOOT_SECTOR_OUTER_VOLUME_BAK_HEADER_CRC_SIZE - - WHIRLPOOL_init (&whirlpool); - sha512_begin (&sha2); - // read the first 512 bytes - offset.QuadPart = 0; - - status = TCReadDevice (LowerDeviceObject, ioBuffer, offset, TC_SECTOR_SIZE_BIOS); - if (NT_SUCCESS (status)) - { - WHIRLPOOL_add (ioBuffer, TC_BOOT_SECTOR_PIM_VALUE_OFFSET * 8, &whirlpool); - WHIRLPOOL_add (ioBuffer + TC_BOOT_SECTOR_USER_MESSAGE_OFFSET + TC_BOOT_SECTOR_USER_MESSAGE_MAX_LENGTH, (TC_BOOT_SECTOR_USER_CONFIG_OFFSET - (TC_BOOT_SECTOR_USER_MESSAGE_OFFSET + TC_BOOT_SECTOR_USER_MESSAGE_MAX_LENGTH)) * 8, &whirlpool); - WHIRLPOOL_add (ioBuffer + TC_BOOT_SECTOR_USER_CONFIG_OFFSET + 1, (TC_MAX_MBR_BOOT_CODE_SIZE - (TC_BOOT_SECTOR_USER_CONFIG_OFFSET + 1)) * 8, &whirlpool); - - sha512_hash (ioBuffer, TC_BOOT_SECTOR_PIM_VALUE_OFFSET, &sha2); - sha512_hash (ioBuffer + TC_BOOT_SECTOR_USER_MESSAGE_OFFSET + TC_BOOT_SECTOR_USER_MESSAGE_MAX_LENGTH, (TC_BOOT_SECTOR_USER_CONFIG_OFFSET - (TC_BOOT_SECTOR_USER_MESSAGE_OFFSET + TC_BOOT_SECTOR_USER_MESSAGE_MAX_LENGTH)), &sha2); - sha512_hash (ioBuffer + TC_BOOT_SECTOR_USER_CONFIG_OFFSET + 1, (TC_MAX_MBR_BOOT_CODE_SIZE - (TC_BOOT_SECTOR_USER_CONFIG_OFFSET + 1)), &sha2); - - // we has the reste of the bootloader, 512 bytes at a time - offset.QuadPart = TC_SECTOR_SIZE_BIOS; - remainingBytes = bootloaderTotalSize - TC_SECTOR_SIZE_BIOS; - - while (NT_SUCCESS (status) && (remainingBytes > 0)) - { - bytesToRead = (remainingBytes >= TC_SECTOR_SIZE_BIOS)? TC_SECTOR_SIZE_BIOS : remainingBytes; - status = TCReadDevice (LowerDeviceObject, ioBuffer, offset, bytesToRead); - if (NT_SUCCESS (status)) - { - remainingBytes -= bytesToRead; - offset.QuadPart += bytesToRead; - WHIRLPOOL_add (ioBuffer, bytesToRead * 8, &whirlpool); - sha512_hash (ioBuffer, bytesToRead, &sha2); - } - else - { - Dump ("TCReadDevice error %x during ComputeBootLoaderFingerprint call\n", status); - break; - } - } - - if (NT_SUCCESS (status)) - { - WHIRLPOOL_finalize (&whirlpool, BootLoaderFingerprint); - sha512_end (&BootLoaderFingerprint [WHIRLPOOL_DIGESTSIZE], &sha2); - } - } - else - { - Dump ("TCReadDevice error %x during ComputeBootLoaderFingerprint call\n", status); - } -} - - -static NTSTATUS MountDrive (DriveFilterExtension *Extension, Password *password, uint32 *headerSaltCrc32) -{ - BOOL hiddenVolume = (BootArgs.HiddenSystemPartitionStart != 0); - int64 hiddenHeaderOffset = BootArgs.HiddenSystemPartitionStart + TC_HIDDEN_VOLUME_HEADER_OFFSET; - NTSTATUS status; - LARGE_INTEGER offset; - char *header; - int pkcs5_prf = 0, pim = 0; - byte *mappedCryptoInfo = NULL; - - Dump ("MountDrive pdo=%p\n", Extension->Pdo); - ASSERT (KeGetCurrentIrql() == PASSIVE_LEVEL); - - // Check boot drive signature first (header CRC search could fail if a user restored the header to a non-boot drive) - if (BootDriveSignatureValid) - { - byte mbr[TC_SECTOR_SIZE_BIOS]; - - offset.QuadPart = 0; - status = TCReadDevice (Extension->LowerDeviceObject, mbr, offset, TC_SECTOR_SIZE_BIOS); - - if (NT_SUCCESS (status) && BootArgs.BootDriveSignature != *(uint32 *) (mbr + 0x1b8)) - return STATUS_UNSUCCESSFUL; - } - - header = TCalloc (TC_BOOT_ENCRYPTION_VOLUME_HEADER_SIZE); - if (!header) - return STATUS_INSUFFICIENT_RESOURCES; - - offset.QuadPart = hiddenVolume ? hiddenHeaderOffset : TC_BOOT_VOLUME_HEADER_SECTOR_OFFSET; - Dump ("Reading volume header at %I64u\n", offset.QuadPart); - - status = TCReadDevice (Extension->LowerDeviceObject, header, offset, TC_BOOT_ENCRYPTION_VOLUME_HEADER_SIZE); - if (!NT_SUCCESS (status)) - { - Dump ("TCReadDevice error %x\n", status); - goto ret; - } - - if (headerSaltCrc32) - { - uint32 saltCrc = GetCrc32 (header, PKCS5_SALT_SIZE); - - if (saltCrc != *headerSaltCrc32) - { - status = STATUS_UNSUCCESSFUL; - goto ret; - } - - Extension->VolumeHeaderSaltCrc32 = saltCrc; - } - - Extension->HeaderCryptoInfo = crypto_open(); - if (!Extension->HeaderCryptoInfo) - { - status = STATUS_INSUFFICIENT_RESOURCES; - goto ret; - } - - if (BootArgs.CryptoInfoLength > 0) - { - PHYSICAL_ADDRESS cryptoInfoAddress; - - cryptoInfoAddress.QuadPart = (BootLoaderSegment << 4) + BootArgs.CryptoInfoOffset; -#ifdef DEBUG - Dump ("Wiping memory %x %d\n", cryptoInfoAddress.LowPart, BootArgs.CryptoInfoLength); -#endif - mappedCryptoInfo = MmMapIoSpace (cryptoInfoAddress, BootArgs.CryptoInfoLength, MmCached); - if (mappedCryptoInfo) - { - /* Get the parameters used for booting to speed up driver startup and avoid testing irrelevant PRFs */ - BOOT_CRYPTO_HEADER* pBootCryptoInfo = (BOOT_CRYPTO_HEADER*) mappedCryptoInfo; - Hash* pHash = HashGet(pBootCryptoInfo->pkcs5); - if (pHash && pHash->SystemEncryption) - pkcs5_prf = pBootCryptoInfo->pkcs5; - } - } - - pim = (int) (BootArgs.Flags >> 16); - - if (ReadVolumeHeader (!hiddenVolume, header, password, pkcs5_prf, pim, FALSE, &Extension->Queue.CryptoInfo, Extension->HeaderCryptoInfo) == 0) - { - // Header decrypted - status = STATUS_SUCCESS; - Dump ("Header decrypted\n"); - - // calculate Fingerprint - ComputeBootLoaderFingerprint (Extension->LowerDeviceObject, header); - - if (Extension->Queue.CryptoInfo->hiddenVolume) - { - int64 hiddenPartitionOffset = BootArgs.HiddenSystemPartitionStart; - Dump ("Hidden volume start offset = %I64d\n", Extension->Queue.CryptoInfo->EncryptedAreaStart.Value + hiddenPartitionOffset); - - Extension->HiddenSystem = TRUE; - - Extension->Queue.RemapEncryptedArea = TRUE; - Extension->Queue.RemappedAreaOffset = hiddenPartitionOffset + Extension->Queue.CryptoInfo->EncryptedAreaStart.Value - BootArgs.DecoySystemPartitionStart; - Extension->Queue.RemappedAreaDataUnitOffset = Extension->Queue.CryptoInfo->EncryptedAreaStart.Value / ENCRYPTION_DATA_UNIT_SIZE - BootArgs.DecoySystemPartitionStart / ENCRYPTION_DATA_UNIT_SIZE; - - Extension->Queue.CryptoInfo->EncryptedAreaStart.Value = BootArgs.DecoySystemPartitionStart; - - if (Extension->Queue.CryptoInfo->VolumeSize.Value > hiddenPartitionOffset - BootArgs.DecoySystemPartitionStart) - TC_THROW_FATAL_EXCEPTION; - - Dump ("RemappedAreaOffset = %I64d\n", Extension->Queue.RemappedAreaOffset); - Dump ("RemappedAreaDataUnitOffset = %I64d\n", Extension->Queue.RemappedAreaDataUnitOffset); - } - else - { - Extension->HiddenSystem = FALSE; - Extension->Queue.RemapEncryptedArea = FALSE; - } - - Extension->ConfiguredEncryptedAreaStart = Extension->Queue.CryptoInfo->EncryptedAreaStart.Value; - Extension->ConfiguredEncryptedAreaEnd = Extension->Queue.CryptoInfo->EncryptedAreaStart.Value + Extension->Queue.CryptoInfo->VolumeSize.Value - 1; - - Extension->Queue.EncryptedAreaStart = Extension->Queue.CryptoInfo->EncryptedAreaStart.Value; - Extension->Queue.EncryptedAreaEnd = Extension->Queue.CryptoInfo->EncryptedAreaStart.Value + Extension->Queue.CryptoInfo->EncryptedAreaLength.Value - 1; - - if (Extension->Queue.CryptoInfo->EncryptedAreaLength.Value == 0) - { - Extension->Queue.EncryptedAreaStart = -1; - Extension->Queue.EncryptedAreaEnd = -1; - } - - Dump ("Loaded: ConfiguredEncryptedAreaStart=%I64d (%I64d) ConfiguredEncryptedAreaEnd=%I64d (%I64d)\n", Extension->ConfiguredEncryptedAreaStart / 1024 / 1024, Extension->ConfiguredEncryptedAreaStart, Extension->ConfiguredEncryptedAreaEnd / 1024 / 1024, Extension->ConfiguredEncryptedAreaEnd); - Dump ("Loaded: EncryptedAreaStart=%I64d (%I64d) EncryptedAreaEnd=%I64d (%I64d)\n", Extension->Queue.EncryptedAreaStart / 1024 / 1024, Extension->Queue.EncryptedAreaStart, Extension->Queue.EncryptedAreaEnd / 1024 / 1024, Extension->Queue.EncryptedAreaEnd); - - // Erase boot loader scheduled keys - if (mappedCryptoInfo) - { - burn (mappedCryptoInfo, BootArgs.CryptoInfoLength); - MmUnmapIoSpace (mappedCryptoInfo, BootArgs.CryptoInfoLength); - BootArgs.CryptoInfoLength = 0; - } - - BootDriveFilterExtension = Extension; - BootDriveFound = Extension->BootDrive = Extension->DriveMounted = Extension->VolumeHeaderPresent = TRUE; - BootDriveFilterExtension->MagicNumber = TC_BOOT_DRIVE_FILTER_EXTENSION_MAGIC_NUMBER; - - burn (&BootArgs.BootPassword, sizeof (BootArgs.BootPassword)); - - { - STORAGE_DEVICE_NUMBER storageDeviceNumber; - status = SendDeviceIoControlRequest (Extension->LowerDeviceObject, IOCTL_STORAGE_GET_DEVICE_NUMBER, NULL, 0, &storageDeviceNumber, sizeof (storageDeviceNumber)); - - if (!NT_SUCCESS (status)) - { - Dump ("Failed to get drive number - error %x\n", status); - Extension->SystemStorageDeviceNumberValid = FALSE; - } - else - { - Extension->SystemStorageDeviceNumber = storageDeviceNumber.DeviceNumber; - Extension->SystemStorageDeviceNumberValid = TRUE; - } - } - - status = SendDeviceIoControlRequest (Extension->LowerDeviceObject, IOCTL_DISK_GET_LENGTH_INFO, NULL, 0, &BootDriveLength, sizeof (BootDriveLength)); - - if (!NT_SUCCESS (status)) - { - Dump ("Failed to get drive length - error %x\n", status); - BootDriveLength.QuadPart = 0; - Extension->Queue.MaxReadAheadOffset.QuadPart = 0; - } - else - Extension->Queue.MaxReadAheadOffset = BootDriveLength; - - status = EncryptedIoQueueStart (&Extension->Queue); - if (!NT_SUCCESS (status)) - TC_BUG_CHECK (status); - - if (IsOSAtLeast (WIN_VISTA)) - { - CrashDumpEnabled = TRUE; - HibernationEnabled = TRUE; - } - else if (!LegacyHibernationDriverFilterActive) - StartLegacyHibernationDriverFilter(); - - // Hidden system hibernation is not supported if an extra boot partition is present as the system is not allowed to update the boot partition - if (IsHiddenSystemRunning() && (BootArgs.Flags & TC_BOOT_ARGS_FLAG_EXTRA_BOOT_PARTITION)) - { - CrashDumpEnabled = FALSE; - HibernationEnabled = FALSE; - } - } - else - { - Dump ("Header not decrypted\n"); - crypto_close (Extension->HeaderCryptoInfo); - Extension->HeaderCryptoInfo = NULL; - - status = STATUS_UNSUCCESSFUL; - } - -ret: - TCfree (header); - return status; -} - - -static NTSTATUS SaveDriveVolumeHeader (DriveFilterExtension *Extension) -{ - NTSTATUS status = STATUS_SUCCESS; - LARGE_INTEGER offset; - byte *header; - - header = TCalloc (TC_BOOT_ENCRYPTION_VOLUME_HEADER_SIZE); - if (!header) - return STATUS_INSUFFICIENT_RESOURCES; - - offset.QuadPart = TC_BOOT_VOLUME_HEADER_SECTOR_OFFSET; - - status = TCReadDevice (Extension->LowerDeviceObject, header, offset, TC_BOOT_ENCRYPTION_VOLUME_HEADER_SIZE); - if (!NT_SUCCESS (status)) - { - Dump ("TCReadDevice error %x", status); - goto ret; - } - - Dump ("Saving: ConfiguredEncryptedAreaStart=%I64d (%I64d) ConfiguredEncryptedAreaEnd=%I64d (%I64d)\n", Extension->ConfiguredEncryptedAreaStart / 1024 / 1024, Extension->ConfiguredEncryptedAreaStart, Extension->ConfiguredEncryptedAreaEnd / 1024 / 1024, Extension->ConfiguredEncryptedAreaEnd); - Dump ("Saving: EncryptedAreaStart=%I64d (%I64d) EncryptedAreaEnd=%I64d (%I64d)\n", Extension->Queue.EncryptedAreaStart / 1024 / 1024, Extension->Queue.EncryptedAreaStart, Extension->Queue.EncryptedAreaEnd / 1024 / 1024, Extension->Queue.EncryptedAreaEnd); - - if (Extension->Queue.EncryptedAreaStart == -1 || Extension->Queue.EncryptedAreaEnd == -1 - || Extension->Queue.EncryptedAreaEnd <= Extension->Queue.EncryptedAreaStart) - { - if (SetupRequest.SetupMode == SetupDecryption) - { - memset (header, 0, TC_BOOT_ENCRYPTION_VOLUME_HEADER_SIZE); - Extension->VolumeHeaderPresent = FALSE; - } - } - else - { - uint32 headerCrc32; - uint64 encryptedAreaLength = Extension->Queue.EncryptedAreaEnd + 1 - Extension->Queue.EncryptedAreaStart; - byte *fieldPos = header + TC_HEADER_OFFSET_ENCRYPTED_AREA_LENGTH; - - DecryptBuffer (header + HEADER_ENCRYPTED_DATA_OFFSET, HEADER_ENCRYPTED_DATA_SIZE, Extension->HeaderCryptoInfo); - - if (GetHeaderField32 (header, TC_HEADER_OFFSET_MAGIC) != 0x56455241) - { - Dump ("Header not decrypted"); - status = STATUS_UNKNOWN_REVISION; - goto ret; - } - - mputInt64 (fieldPos, encryptedAreaLength); - - headerCrc32 = GetCrc32 (header + TC_HEADER_OFFSET_MAGIC, TC_HEADER_OFFSET_HEADER_CRC - TC_HEADER_OFFSET_MAGIC); - fieldPos = header + TC_HEADER_OFFSET_HEADER_CRC; - mputLong (fieldPos, headerCrc32); - - EncryptBuffer (header + HEADER_ENCRYPTED_DATA_OFFSET, HEADER_ENCRYPTED_DATA_SIZE, Extension->HeaderCryptoInfo); - } - - status = TCWriteDevice (Extension->LowerDeviceObject, header, offset, TC_BOOT_ENCRYPTION_VOLUME_HEADER_SIZE); - if (!NT_SUCCESS (status)) - { - Dump ("TCWriteDevice error %x", status); - goto ret; - } - -ret: - TCfree (header); - return status; -} - - -static NTSTATUS PassIrp (PDEVICE_OBJECT deviceObject, PIRP irp) -{ - IoSkipCurrentIrpStackLocation (irp); - return IoCallDriver (deviceObject, irp); -} - - -static NTSTATUS PassFilteredIrp (PDEVICE_OBJECT deviceObject, PIRP irp, PIO_COMPLETION_ROUTINE completionRoutine, PVOID completionRoutineArg) -{ - IoCopyCurrentIrpStackLocationToNext (irp); - - if (completionRoutine) - IoSetCompletionRoutine (irp, completionRoutine, completionRoutineArg, TRUE, TRUE, TRUE); - - return IoCallDriver (deviceObject, irp); -} - - -static NTSTATUS OnDeviceUsageNotificationCompleted (PDEVICE_OBJECT filterDeviceObject, PIRP Irp, DriveFilterExtension *Extension) -{ - if (Irp->PendingReturned) - IoMarkIrpPending (Irp); - - if (!(Extension->LowerDeviceObject->Flags & DO_POWER_PAGABLE)) - filterDeviceObject->Flags &= ~DO_POWER_PAGABLE; - - IoReleaseRemoveLock (&Extension->Queue.RemoveLock, Irp); - return STATUS_CONTINUE_COMPLETION; -} - - -static BOOL IsVolumeDevice (PDEVICE_OBJECT deviceObject) -{ - VOLUME_NUMBER volNumber; - VOLUME_DISK_EXTENTS extents[2]; - NTSTATUS extentStatus = SendDeviceIoControlRequest (deviceObject, IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS, NULL, 0, extents, sizeof (extents)); - - return NT_SUCCESS (SendDeviceIoControlRequest (deviceObject, IOCTL_VOLUME_SUPPORTS_ONLINE_OFFLINE, NULL, 0, NULL, 0)) - || NT_SUCCESS (SendDeviceIoControlRequest (deviceObject, IOCTL_VOLUME_IS_OFFLINE, NULL, 0, NULL, 0)) - || NT_SUCCESS (SendDeviceIoControlRequest (deviceObject, IOCTL_VOLUME_IS_IO_CAPABLE, NULL, 0, NULL, 0)) - || NT_SUCCESS (SendDeviceIoControlRequest (deviceObject, IOCTL_VOLUME_IS_PARTITION, NULL, 0, NULL, 0)) - || NT_SUCCESS (SendDeviceIoControlRequest (deviceObject, IOCTL_VOLUME_QUERY_VOLUME_NUMBER, NULL, 0, &volNumber, sizeof (volNumber))) - || NT_SUCCESS (extentStatus) || extentStatus == STATUS_BUFFER_OVERFLOW || extentStatus == STATUS_BUFFER_TOO_SMALL; -} - - -static void CheckDeviceTypeAndMount (DriveFilterExtension *filterExtension) -{ - if (BootArgsValid) - { - // Windows sometimes merges a removable drive PDO and its volume PDO to a single PDO having no volume interface (GUID_DEVINTERFACE_VOLUME). - // Therefore, we need to test whether the device supports volume IOCTLs. - if (VolumeClassFilterRegistered - && BootArgs.HiddenSystemPartitionStart != 0 - && IsVolumeDevice (filterExtension->LowerDeviceObject)) - { - Dump ("Drive and volume merged pdo=%p", filterExtension->Pdo); - - filterExtension->IsVolumeFilterDevice = TRUE; - filterExtension->IsDriveFilterDevice = FALSE; - } - else - { - NTSTATUS status = KeWaitForMutexObject (&MountMutex, Executive, KernelMode, FALSE, NULL); - if (!NT_SUCCESS (status)) - TC_BUG_CHECK (status); - - if (!BootDriveFound) - MountDrive (filterExtension, &BootArgs.BootPassword, &BootArgs.HeaderSaltCrc32); - - KeReleaseMutex (&MountMutex, FALSE); - } - } -} - - -static VOID MountDriveWorkItemRoutine (PDEVICE_OBJECT deviceObject, DriveFilterExtension *filterExtension) -{ - CheckDeviceTypeAndMount (filterExtension); - KeSetEvent (&filterExtension->MountWorkItemCompletedEvent, IO_NO_INCREMENT, FALSE); -} - - -static NTSTATUS OnStartDeviceCompleted (PDEVICE_OBJECT filterDeviceObject, PIRP Irp, DriveFilterExtension *Extension) -{ - if (Irp->PendingReturned) - IoMarkIrpPending (Irp); - - if (Extension->LowerDeviceObject->Characteristics & FILE_REMOVABLE_MEDIA) - filterDeviceObject->Characteristics |= FILE_REMOVABLE_MEDIA; - - if (KeGetCurrentIrql() == PASSIVE_LEVEL) - { - CheckDeviceTypeAndMount (Extension); - } - else - { - PIO_WORKITEM workItem = IoAllocateWorkItem (filterDeviceObject); - if (!workItem) - { - IoReleaseRemoveLock (&Extension->Queue.RemoveLock, Irp); - return STATUS_INSUFFICIENT_RESOURCES; - } - - KeInitializeEvent (&Extension->MountWorkItemCompletedEvent, SynchronizationEvent, FALSE); - IoQueueWorkItem (workItem, MountDriveWorkItemRoutine, DelayedWorkQueue, Extension); - - KeWaitForSingleObject (&Extension->MountWorkItemCompletedEvent, Executive, KernelMode, FALSE, NULL); - IoFreeWorkItem (workItem); - } - - IoReleaseRemoveLock (&Extension->Queue.RemoveLock, Irp); - return STATUS_CONTINUE_COMPLETION; -} - - -static NTSTATUS DispatchPnp (PDEVICE_OBJECT DeviceObject, PIRP Irp, DriveFilterExtension *Extension, PIO_STACK_LOCATION irpSp) -{ - NTSTATUS status; - - status = IoAcquireRemoveLock (&Extension->Queue.RemoveLock, Irp); - if (!NT_SUCCESS (status)) - return TCCompleteIrp (Irp, status, 0); - - switch (irpSp->MinorFunction) - { - case IRP_MN_START_DEVICE: - Dump ("IRP_MN_START_DEVICE pdo=%p\n", Extension->Pdo); - return PassFilteredIrp (Extension->LowerDeviceObject, Irp, OnStartDeviceCompleted, Extension); - - - case IRP_MN_DEVICE_USAGE_NOTIFICATION: - Dump ("IRP_MN_DEVICE_USAGE_NOTIFICATION type=%d\n", (int) irpSp->Parameters.UsageNotification.Type); - - { - PDEVICE_OBJECT attachedDevice = IoGetAttachedDeviceReference (DeviceObject); - - if (attachedDevice == DeviceObject || (attachedDevice->Flags & DO_POWER_PAGABLE)) - DeviceObject->Flags |= DO_POWER_PAGABLE; - - ObDereferenceObject (attachedDevice); - } - - // Prevent creation of hibernation and crash dump files if required - if (irpSp->Parameters.UsageNotification.InPath - && ( - (irpSp->Parameters.UsageNotification.Type == DeviceUsageTypeDumpFile && !CrashDumpEnabled) - || (irpSp->Parameters.UsageNotification.Type == DeviceUsageTypeHibernation && !HibernationEnabled) - ) - ) - { - IoReleaseRemoveLock (&Extension->Queue.RemoveLock, Irp); - - if (irpSp->Parameters.UsageNotification.Type == DeviceUsageTypeHibernation) - ++HibernationPreventionCount; - - Dump ("Preventing dump type=%d\n", (int) irpSp->Parameters.UsageNotification.Type); - return TCCompleteIrp (Irp, STATUS_UNSUCCESSFUL, 0); - } - - return PassFilteredIrp (Extension->LowerDeviceObject, Irp, OnDeviceUsageNotificationCompleted, Extension); - - - case IRP_MN_REMOVE_DEVICE: - Dump ("IRP_MN_REMOVE_DEVICE pdo=%p\n", Extension->Pdo); - - IoReleaseRemoveLockAndWait (&Extension->Queue.RemoveLock, Irp); - status = PassIrp (Extension->LowerDeviceObject, Irp); - - IoDetachDevice (Extension->LowerDeviceObject); - - if (Extension->DriveMounted) - DismountDrive (Extension, TRUE); - - if (Extension->BootDrive) - { - BootDriveFound = FALSE; - BootDriveFilterExtension = NULL; - } - - IoDeleteDevice (DeviceObject); - return status; - - - default: - status = PassIrp (Extension->LowerDeviceObject, Irp); - IoReleaseRemoveLock (&Extension->Queue.RemoveLock, Irp); - } - return status; -} - - -static NTSTATUS DispatchPower (PDEVICE_OBJECT DeviceObject, PIRP Irp, DriveFilterExtension *Extension, PIO_STACK_LOCATION irpSp) -{ - NTSTATUS status; - Dump ("IRP_MJ_POWER minor=%d type=%d shutdown=%d\n", (int) irpSp->MinorFunction, (int) irpSp->Parameters.Power.Type, (int) irpSp->Parameters.Power.ShutdownType); - - if (SetupInProgress - && irpSp->MinorFunction == IRP_MN_SET_POWER - && irpSp->Parameters.Power.ShutdownType == PowerActionHibernate) - { - while (SendDeviceIoControlRequest (RootDeviceObject, TC_IOCTL_ABORT_BOOT_ENCRYPTION_SETUP, NULL, 0, NULL, 0) == STATUS_INSUFFICIENT_RESOURCES); - } - -#if 0 // Dismount of the system drive is disabled until there is a way to do it without causing system errors (see the documentation for more info) - if (DriverShuttingDown - && Extension->BootDrive - && Extension->DriveMounted - && irpSp->MinorFunction == IRP_MN_SET_POWER - && irpSp->Parameters.Power.Type == DevicePowerState) - { - DismountDrive (Extension, TRUE); - } -#endif // 0 - - PoStartNextPowerIrp (Irp); - - status = IoAcquireRemoveLock (&Extension->Queue.RemoveLock, Irp); - if (!NT_SUCCESS (status)) - return TCCompleteIrp (Irp, status, 0); - - IoSkipCurrentIrpStackLocation (Irp); - status = PoCallDriver (Extension->LowerDeviceObject, Irp); - - IoReleaseRemoveLock (&Extension->Queue.RemoveLock, Irp); - return status; -} - - -NTSTATUS DriveFilterDispatchIrp (PDEVICE_OBJECT DeviceObject, PIRP Irp) -{ - DriveFilterExtension *Extension = (DriveFilterExtension *) DeviceObject->DeviceExtension; - PIO_STACK_LOCATION irpSp = IoGetCurrentIrpStackLocation (Irp); - NTSTATUS status; - - ASSERT (!Extension->bRootDevice && Extension->IsDriveFilterDevice); - - switch (irpSp->MajorFunction) - { - case IRP_MJ_READ: - case IRP_MJ_WRITE: - if (Extension->BootDrive) - { - status = EncryptedIoQueueAddIrp (&Extension->Queue, Irp); - - if (status != STATUS_PENDING) - TCCompleteDiskIrp (Irp, status, 0); - - return status; - } - break; - - case IRP_MJ_PNP: - return DispatchPnp (DeviceObject, Irp, Extension, irpSp); - - case IRP_MJ_POWER: - return DispatchPower (DeviceObject, Irp, Extension, irpSp); - } - - status = IoAcquireRemoveLock (&Extension->Queue.RemoveLock, Irp); - if (!NT_SUCCESS (status)) - return TCCompleteIrp (Irp, status, 0); - - status = PassIrp (Extension->LowerDeviceObject, Irp); - - IoReleaseRemoveLock (&Extension->Queue.RemoveLock, Irp); - return status; -} - - -void ReopenBootVolumeHeader (PIRP irp, PIO_STACK_LOCATION irpSp) -{ - LARGE_INTEGER offset; - char *header; - ReopenBootVolumeHeaderRequest *request = (ReopenBootVolumeHeaderRequest *) irp->AssociatedIrp.SystemBuffer; - - irp->IoStatus.Information = 0; - - if (!IoIsSystemThread (PsGetCurrentThread()) && !UserCanAccessDriveDevice()) - { - irp->IoStatus.Status = STATUS_ACCESS_DENIED; - return; - } - - if (!ValidateIOBufferSize (irp, sizeof (ReopenBootVolumeHeaderRequest), ValidateInput)) - return; - - if (!BootDriveFound || !BootDriveFilterExtension || !BootDriveFilterExtension->DriveMounted || !BootDriveFilterExtension->HeaderCryptoInfo - || request->VolumePassword.Length > MAX_PASSWORD - || request->pkcs5_prf < 0 - || request->pkcs5_prf > LAST_PRF_ID - || request->pim < 0 - || request->pim > 65535 - ) - { - irp->IoStatus.Status = STATUS_INVALID_PARAMETER; - goto wipe; - } - - header = TCalloc (TC_BOOT_ENCRYPTION_VOLUME_HEADER_SIZE); - if (!header) - { - irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES; - goto wipe; - } - - if (BootDriveFilterExtension->HiddenSystem) - offset.QuadPart = BootArgs.HiddenSystemPartitionStart + TC_HIDDEN_VOLUME_HEADER_OFFSET; - else - offset.QuadPart = TC_BOOT_VOLUME_HEADER_SECTOR_OFFSET; - - irp->IoStatus.Status = TCReadDevice (BootDriveFilterExtension->LowerDeviceObject, header, offset, TC_BOOT_ENCRYPTION_VOLUME_HEADER_SIZE); - if (!NT_SUCCESS (irp->IoStatus.Status)) - { - Dump ("TCReadDevice error %x\n", irp->IoStatus.Status); - goto ret; - } - - if (ReadVolumeHeader (!BootDriveFilterExtension->HiddenSystem, header, &request->VolumePassword, request->pkcs5_prf, request->pim, FALSE, NULL, BootDriveFilterExtension->HeaderCryptoInfo) == 0) - { - Dump ("Header reopened\n"); - ComputeBootLoaderFingerprint (BootDriveFilterExtension->LowerDeviceObject, header); - - BootDriveFilterExtension->Queue.CryptoInfo->header_creation_time = BootDriveFilterExtension->HeaderCryptoInfo->header_creation_time; - BootDriveFilterExtension->Queue.CryptoInfo->pkcs5 = BootDriveFilterExtension->HeaderCryptoInfo->pkcs5; - BootDriveFilterExtension->Queue.CryptoInfo->noIterations = BootDriveFilterExtension->HeaderCryptoInfo->noIterations; - BootDriveFilterExtension->Queue.CryptoInfo->volumePim = BootDriveFilterExtension->HeaderCryptoInfo->volumePim; - - irp->IoStatus.Status = STATUS_SUCCESS; - } - else - { - crypto_close (BootDriveFilterExtension->HeaderCryptoInfo); - BootDriveFilterExtension->HeaderCryptoInfo = NULL; - - Dump ("Header not reopened\n"); - irp->IoStatus.Status = STATUS_INVALID_PARAMETER; - } - -ret: - TCfree (header); -wipe: - burn (request, sizeof (*request)); -} - - -// Legacy Windows XP/2003 hibernation dump filter - -typedef NTSTATUS (*HiberDriverWriteFunctionA) (ULONG arg0, PLARGE_INTEGER writeOffset, PMDL dataMdl, PVOID arg3); -typedef NTSTATUS (*HiberDriverWriteFunctionB) (PLARGE_INTEGER writeOffset, PMDL dataMdl); - -typedef struct -{ -#ifdef _WIN64 - byte FieldPad1[64]; - HiberDriverWriteFunctionB WriteFunctionB; - byte FieldPad2[56]; -#else - byte FieldPad1[48]; - HiberDriverWriteFunctionB WriteFunctionB; - byte FieldPad2[32]; -#endif - HiberDriverWriteFunctionA WriteFunctionA; - byte FieldPad3[24]; - LARGE_INTEGER PartitionStartOffset; -} HiberDriverContext; - -typedef NTSTATUS (*HiberDriverEntry) (PVOID arg0, HiberDriverContext *hiberDriverContext); - -typedef struct -{ - LIST_ENTRY ModuleList; -#ifdef _WIN64 - byte FieldPad1[32]; -#else - byte FieldPad1[16]; -#endif - PVOID ModuleBaseAddress; - HiberDriverEntry ModuleEntryAddress; -#ifdef _WIN64 - byte FieldPad2[24]; -#else - byte FieldPad2[12]; -#endif - UNICODE_STRING ModuleName; -} ModuleTableItem; - - -#define TC_MAX_HIBER_FILTER_COUNT 3 -static int LastHiberFilterNumber = 0; - -static HiberDriverEntry OriginalHiberDriverEntries[TC_MAX_HIBER_FILTER_COUNT]; -static HiberDriverWriteFunctionA OriginalHiberDriverWriteFunctionsA[TC_MAX_HIBER_FILTER_COUNT]; -static HiberDriverWriteFunctionB OriginalHiberDriverWriteFunctionsB[TC_MAX_HIBER_FILTER_COUNT]; - -static LARGE_INTEGER HiberPartitionOffset; - - -static NTSTATUS HiberDriverWriteFunctionFilter (int filterNumber, PLARGE_INTEGER writeOffset, PMDL dataMdl, BOOL writeB, ULONG arg0WriteA, PVOID arg3WriteA) -{ - MDL *encryptedDataMdl = dataMdl; - - if (writeOffset && dataMdl && BootDriveFilterExtension && BootDriveFilterExtension->DriveMounted) - { - ULONG dataLength = MmGetMdlByteCount (dataMdl); - - if (dataMdl->MappedSystemVa && dataLength > 0) - { - uint64 offset = HiberPartitionOffset.QuadPart + writeOffset->QuadPart; - uint64 intersectStart; - uint32 intersectLength; - - if (dataLength > TC_HIBERNATION_WRITE_BUFFER_SIZE) - TC_BUG_CHECK (STATUS_BUFFER_OVERFLOW); - - if ((dataLength & (ENCRYPTION_DATA_UNIT_SIZE - 1)) != 0) - TC_BUG_CHECK (STATUS_INVALID_PARAMETER); - - if ((offset & (ENCRYPTION_DATA_UNIT_SIZE - 1)) != 0) - TC_BUG_CHECK (STATUS_INVALID_PARAMETER); - - GetIntersection (offset, - dataLength, - BootDriveFilterExtension->Queue.EncryptedAreaStart, - BootDriveFilterExtension->Queue.EncryptedAreaEnd, - &intersectStart, - &intersectLength); - - if (intersectLength > 0) - { - UINT64_STRUCT dataUnit; - dataUnit.Value = intersectStart / ENCRYPTION_DATA_UNIT_SIZE; - - memcpy (HibernationWriteBuffer, dataMdl->MappedSystemVa, dataLength); - - if (BootDriveFilterExtension->Queue.RemapEncryptedArea) - dataUnit.Value += BootDriveFilterExtension->Queue.RemappedAreaDataUnitOffset; - - EncryptDataUnitsCurrentThread (HibernationWriteBuffer + (intersectStart - offset), - &dataUnit, - intersectLength / ENCRYPTION_DATA_UNIT_SIZE, - BootDriveFilterExtension->Queue.CryptoInfo); - - encryptedDataMdl = HibernationWriteBufferMdl; - MmInitializeMdl (encryptedDataMdl, HibernationWriteBuffer, dataLength); - encryptedDataMdl->MdlFlags = dataMdl->MdlFlags; - } - } - } - - if (writeB) - return (*OriginalHiberDriverWriteFunctionsB[filterNumber]) (writeOffset, encryptedDataMdl); - - return (*OriginalHiberDriverWriteFunctionsA[filterNumber]) (arg0WriteA, writeOffset, encryptedDataMdl, arg3WriteA); -} - - -static NTSTATUS HiberDriverWriteFunctionAFilter0 (ULONG arg0, PLARGE_INTEGER writeOffset, PMDL dataMdl, PVOID arg3) -{ - return HiberDriverWriteFunctionFilter (0, writeOffset, dataMdl, FALSE, arg0, arg3); -} - -static NTSTATUS HiberDriverWriteFunctionAFilter1 (ULONG arg0, PLARGE_INTEGER writeOffset, PMDL dataMdl, PVOID arg3) -{ - return HiberDriverWriteFunctionFilter (1, writeOffset, dataMdl, FALSE, arg0, arg3); -} - -static NTSTATUS HiberDriverWriteFunctionAFilter2 (ULONG arg0, PLARGE_INTEGER writeOffset, PMDL dataMdl, PVOID arg3) -{ - return HiberDriverWriteFunctionFilter (2, writeOffset, dataMdl, FALSE, arg0, arg3); -} - - -static NTSTATUS HiberDriverWriteFunctionBFilter0 (PLARGE_INTEGER writeOffset, PMDL dataMdl) -{ - return HiberDriverWriteFunctionFilter (0, writeOffset, dataMdl, TRUE, 0, NULL); -} - -static NTSTATUS HiberDriverWriteFunctionBFilter1 (PLARGE_INTEGER writeOffset, PMDL dataMdl) -{ - return HiberDriverWriteFunctionFilter (1, writeOffset, dataMdl, TRUE, 0, NULL); -} - -static NTSTATUS HiberDriverWriteFunctionBFilter2 (PLARGE_INTEGER writeOffset, PMDL dataMdl) -{ - return HiberDriverWriteFunctionFilter (2, writeOffset, dataMdl, TRUE, 0, NULL); -} - - -static NTSTATUS HiberDriverEntryFilter (int filterNumber, PVOID arg0, HiberDriverContext *hiberDriverContext) -{ - BOOL filterInstalled = FALSE; - NTSTATUS status; - - if (!OriginalHiberDriverEntries[filterNumber]) - return STATUS_UNSUCCESSFUL; - - status = (*OriginalHiberDriverEntries[filterNumber]) (arg0, hiberDriverContext); - - if (!NT_SUCCESS (status) || !hiberDriverContext) - return status; - - if (SetupInProgress) - TC_BUG_CHECK (STATUS_INVALID_PARAMETER); - - if (hiberDriverContext->WriteFunctionA) - { - Dump ("Filtering WriteFunctionA %d\n", filterNumber); - OriginalHiberDriverWriteFunctionsA[filterNumber] = hiberDriverContext->WriteFunctionA; - - switch (filterNumber) - { - case 0: hiberDriverContext->WriteFunctionA = HiberDriverWriteFunctionAFilter0; break; - case 1: hiberDriverContext->WriteFunctionA = HiberDriverWriteFunctionAFilter1; break; - case 2: hiberDriverContext->WriteFunctionA = HiberDriverWriteFunctionAFilter2; break; - default: TC_THROW_FATAL_EXCEPTION; - } - - filterInstalled = TRUE; - } - - if (hiberDriverContext->WriteFunctionB) - { - Dump ("Filtering WriteFunctionB %d\n", filterNumber); - OriginalHiberDriverWriteFunctionsB[filterNumber] = hiberDriverContext->WriteFunctionB; - - switch (filterNumber) - { - case 0: hiberDriverContext->WriteFunctionB = HiberDriverWriteFunctionBFilter0; break; - case 1: hiberDriverContext->WriteFunctionB = HiberDriverWriteFunctionBFilter1; break; - case 2: hiberDriverContext->WriteFunctionB = HiberDriverWriteFunctionBFilter2; break; - default: TC_THROW_FATAL_EXCEPTION; - } - - filterInstalled = TRUE; - } - - if (filterInstalled && hiberDriverContext->PartitionStartOffset.QuadPart != 0) - { - HiberPartitionOffset = hiberDriverContext->PartitionStartOffset; - - if (BootDriveFilterExtension->Queue.RemapEncryptedArea) - hiberDriverContext->PartitionStartOffset.QuadPart += BootDriveFilterExtension->Queue.RemappedAreaOffset; - } - - return STATUS_SUCCESS; -} - - -static NTSTATUS HiberDriverEntryFilter0 (PVOID arg0, HiberDriverContext *hiberDriverContext) -{ - return HiberDriverEntryFilter (0, arg0, hiberDriverContext); -} - - -static NTSTATUS HiberDriverEntryFilter1 (PVOID arg0, HiberDriverContext *hiberDriverContext) -{ - return HiberDriverEntryFilter (1, arg0, hiberDriverContext); -} - - -static NTSTATUS HiberDriverEntryFilter2 (PVOID arg0, HiberDriverContext *hiberDriverContext) -{ - return HiberDriverEntryFilter (2, arg0, hiberDriverContext); -} - - -static VOID LoadImageNotifyRoutine (PUNICODE_STRING fullImageName, HANDLE processId, PIMAGE_INFO imageInfo) -{ - ModuleTableItem *moduleItem; - LIST_ENTRY *listEntry; - KIRQL origIrql; - - if (!imageInfo || !imageInfo->SystemModeImage || !imageInfo->ImageBase || !TCDriverObject->DriverSection) - return; - - moduleItem = *(ModuleTableItem **) TCDriverObject->DriverSection; - if (!moduleItem || !moduleItem->ModuleList.Flink) - return; - - // Search loaded system modules for hibernation driver - origIrql = KeRaiseIrqlToDpcLevel(); - - for (listEntry = moduleItem->ModuleList.Flink->Blink; - listEntry && listEntry != TCDriverObject->DriverSection; - listEntry = listEntry->Flink) - { - moduleItem = CONTAINING_RECORD (listEntry, ModuleTableItem, ModuleList); - - if (moduleItem && imageInfo->ImageBase == moduleItem->ModuleBaseAddress) - { - if (moduleItem->ModuleName.Buffer && moduleItem->ModuleName.Length >= 5 * sizeof (wchar_t)) - { - if (memcmp (moduleItem->ModuleName.Buffer, L"hiber", 5 * sizeof (wchar_t)) == 0 - || memcmp (moduleItem->ModuleName.Buffer, L"Hiber", 5 * sizeof (wchar_t)) == 0 - || memcmp (moduleItem->ModuleName.Buffer, L"HIBER", 5 * sizeof (wchar_t)) == 0) - { - HiberDriverEntry filterEntry; - - switch (LastHiberFilterNumber) - { - case 0: filterEntry = HiberDriverEntryFilter0; break; - case 1: filterEntry = HiberDriverEntryFilter1; break; - case 2: filterEntry = HiberDriverEntryFilter2; break; - default: TC_THROW_FATAL_EXCEPTION; - } - - if (moduleItem->ModuleEntryAddress != filterEntry) - { - // Install filter - OriginalHiberDriverEntries[LastHiberFilterNumber] = moduleItem->ModuleEntryAddress; - moduleItem->ModuleEntryAddress = filterEntry; - - if (++LastHiberFilterNumber > TC_MAX_HIBER_FILTER_COUNT - 1) - LastHiberFilterNumber = 0; - } - } - } - break; - } - } - - KeLowerIrql (origIrql); -} - - -void StartLegacyHibernationDriverFilter () -{ - PHYSICAL_ADDRESS highestAcceptableWriteBufferAddr; - NTSTATUS status; - - ASSERT (KeGetCurrentIrql() == PASSIVE_LEVEL); - ASSERT (!IsOSAtLeast (WIN_VISTA)); - - if (!TCDriverObject->DriverSection || !*(ModuleTableItem **) TCDriverObject->DriverSection) - goto err; - - // All buffers required for hibernation must be allocated here -#ifdef _WIN64 - highestAcceptableWriteBufferAddr.QuadPart = 0x7FFffffFFFFULL; -#else - highestAcceptableWriteBufferAddr.QuadPart = 0xffffFFFFULL; -#endif - - HibernationWriteBuffer = MmAllocateContiguousMemory (TC_HIBERNATION_WRITE_BUFFER_SIZE, highestAcceptableWriteBufferAddr); - if (!HibernationWriteBuffer) - goto err; - - HibernationWriteBufferMdl = IoAllocateMdl (HibernationWriteBuffer, TC_HIBERNATION_WRITE_BUFFER_SIZE, FALSE, FALSE, NULL); - if (!HibernationWriteBufferMdl) - goto err; - - MmBuildMdlForNonPagedPool (HibernationWriteBufferMdl); - - status = PsSetLoadImageNotifyRoutine (LoadImageNotifyRoutine); - if (!NT_SUCCESS (status)) - goto err; - - LegacyHibernationDriverFilterActive = TRUE; - CrashDumpEnabled = FALSE; - HibernationEnabled = TRUE; - return; - -err: - LegacyHibernationDriverFilterActive = FALSE; - CrashDumpEnabled = FALSE; - HibernationEnabled = FALSE; - - if (HibernationWriteBufferMdl) - { - IoFreeMdl (HibernationWriteBufferMdl); - HibernationWriteBufferMdl = NULL; - } - - if (HibernationWriteBuffer) - { - MmFreeContiguousMemory (HibernationWriteBuffer); - HibernationWriteBuffer = NULL; - } -} - - -static VOID SetupThreadProc (PVOID threadArg) -{ - DriveFilterExtension *Extension = BootDriveFilterExtension; - - LARGE_INTEGER offset; - UINT64_STRUCT dataUnit; - ULONG setupBlockSize = TC_ENCRYPTION_SETUP_IO_BLOCK_SIZE; - BOOL headerUpdateRequired = FALSE; - int64 bytesWrittenSinceHeaderUpdate = 0; - - byte *buffer = NULL; - byte *wipeBuffer = NULL; - byte wipeRandChars[TC_WIPE_RAND_CHAR_COUNT]; - byte wipeRandCharsUpdate[TC_WIPE_RAND_CHAR_COUNT]; - - KIRQL irql; - NTSTATUS status; - - // generate real random values for wipeRandChars and - // wipeRandCharsUpdate instead of relying on uninitialized stack memory - LARGE_INTEGER iSeed; - KeQuerySystemTime( &iSeed ); - if (KeGetCurrentIrql() < DISPATCH_LEVEL) - { - ULONG ulRandom; - ulRandom = RtlRandomEx( &iSeed.LowPart ); - memcpy (wipeRandChars, &ulRandom, TC_WIPE_RAND_CHAR_COUNT); - ulRandom = RtlRandomEx( &ulRandom ); - memcpy (wipeRandCharsUpdate, &ulRandom, TC_WIPE_RAND_CHAR_COUNT); - burn (&ulRandom, sizeof(ulRandom)); - } - else - { - byte digest[SHA512_DIGESTSIZE]; - sha512_ctx tctx; - sha512_begin (&tctx); - sha512_hash ((unsigned char *) &(iSeed.QuadPart), sizeof(iSeed.QuadPart), &tctx); - sha512_end (digest, &tctx); - - memcpy (wipeRandChars, digest, TC_WIPE_RAND_CHAR_COUNT); - memcpy (wipeRandCharsUpdate, &digest[SHA512_DIGESTSIZE - TC_WIPE_RAND_CHAR_COUNT], TC_WIPE_RAND_CHAR_COUNT); - - burn (digest, SHA512_DIGESTSIZE); - burn (&tctx, sizeof (tctx)); - } - - burn (&iSeed, sizeof(iSeed)); - - SetupResult = STATUS_UNSUCCESSFUL; - - // Make sure volume header can be updated - if (Extension->HeaderCryptoInfo == NULL) - { - SetupResult = STATUS_INVALID_PARAMETER; - goto ret; - } - - buffer = TCalloc (TC_ENCRYPTION_SETUP_IO_BLOCK_SIZE); - if (!buffer) - { - SetupResult = STATUS_INSUFFICIENT_RESOURCES; - goto ret; - } - - if (SetupRequest.SetupMode == SetupEncryption && SetupRequest.WipeAlgorithm != TC_WIPE_NONE) - { - wipeBuffer = TCalloc (TC_ENCRYPTION_SETUP_IO_BLOCK_SIZE); - if (!wipeBuffer) - { - SetupResult = STATUS_INSUFFICIENT_RESOURCES; - goto ret; - } - } - - while (!NT_SUCCESS (EncryptedIoQueueHoldWhenIdle (&Extension->Queue, 1000))) - { - if (EncryptionSetupThreadAbortRequested) - goto abort; - - TransformWaitingForIdle = TRUE; - } - TransformWaitingForIdle = FALSE; - - switch (SetupRequest.SetupMode) - { - case SetupEncryption: - Dump ("Encrypting...\n"); - if (Extension->Queue.EncryptedAreaStart == -1 || Extension->Queue.EncryptedAreaEnd == -1) - { - // Start encryption - Extension->Queue.EncryptedAreaStart = Extension->ConfiguredEncryptedAreaStart; - Extension->Queue.EncryptedAreaEnd = -1; - offset.QuadPart = Extension->ConfiguredEncryptedAreaStart; - } - else - { - // Resume aborted encryption - if (Extension->Queue.EncryptedAreaEnd == Extension->ConfiguredEncryptedAreaEnd) - goto err; - - offset.QuadPart = Extension->Queue.EncryptedAreaEnd + 1; - } - - break; - - case SetupDecryption: - Dump ("Decrypting...\n"); - if (Extension->Queue.EncryptedAreaStart == -1 || Extension->Queue.EncryptedAreaEnd == -1) - { - SetupResult = STATUS_SUCCESS; - goto abort; - } - - offset.QuadPart = Extension->Queue.EncryptedAreaEnd + 1; - break; - - default: - goto err; - } - - EncryptedIoQueueResumeFromHold (&Extension->Queue); - - Dump ("EncryptedAreaStart=%I64d\n", Extension->Queue.EncryptedAreaStart); - Dump ("EncryptedAreaEnd=%I64d\n", Extension->Queue.EncryptedAreaEnd); - Dump ("ConfiguredEncryptedAreaStart=%I64d\n", Extension->ConfiguredEncryptedAreaStart); - Dump ("ConfiguredEncryptedAreaEnd=%I64d\n", Extension->ConfiguredEncryptedAreaEnd); - Dump ("offset=%I64d\n", offset.QuadPart); - Dump ("EncryptedAreaStart=%I64d (%I64d) EncryptedAreaEnd=%I64d\n", Extension->Queue.EncryptedAreaStart / 1024 / 1024, Extension->Queue.EncryptedAreaStart, Extension->Queue.EncryptedAreaEnd / 1024 / 1024); - - while (!EncryptionSetupThreadAbortRequested) - { - if (SetupRequest.SetupMode == SetupEncryption) - { - if (offset.QuadPart + setupBlockSize > Extension->ConfiguredEncryptedAreaEnd + 1) - setupBlockSize = (ULONG) (Extension->ConfiguredEncryptedAreaEnd + 1 - offset.QuadPart); - - if (offset.QuadPart > Extension->ConfiguredEncryptedAreaEnd) - break; - } - else - { - if (offset.QuadPart - setupBlockSize < Extension->Queue.EncryptedAreaStart) - setupBlockSize = (ULONG) (offset.QuadPart - Extension->Queue.EncryptedAreaStart); - - offset.QuadPart -= setupBlockSize; - - if (setupBlockSize == 0 || offset.QuadPart < Extension->Queue.EncryptedAreaStart) - break; - } - - while (!NT_SUCCESS (EncryptedIoQueueHoldWhenIdle (&Extension->Queue, 500))) - { - if (EncryptionSetupThreadAbortRequested) - goto abort; - - TransformWaitingForIdle = TRUE; - } - TransformWaitingForIdle = FALSE; - - status = TCReadDevice (BootDriveFilterExtension->LowerDeviceObject, buffer, offset, setupBlockSize); - if (!NT_SUCCESS (status)) - { - Dump ("TCReadDevice error %x offset=%I64d\n", status, offset.QuadPart); - - if (SetupRequest.ZeroUnreadableSectors && SetupRequest.SetupMode == SetupEncryption) - { - // Zero unreadable sectors - uint64 zeroedSectorCount; - - status = ZeroUnreadableSectors (BootDriveFilterExtension->LowerDeviceObject, offset, setupBlockSize, &zeroedSectorCount); - if (!NT_SUCCESS (status)) - { - SetupResult = status; - goto err; - } - - // Retry read - status = TCReadDevice (BootDriveFilterExtension->LowerDeviceObject, buffer, offset, setupBlockSize); - if (!NT_SUCCESS (status)) - { - SetupResult = status; - goto err; - } - } - else if (SetupRequest.DiscardUnreadableEncryptedSectors && SetupRequest.SetupMode == SetupDecryption) - { - // Discard unreadable encrypted sectors - uint64 badSectorCount; - - status = ReadDeviceSkipUnreadableSectors (BootDriveFilterExtension->LowerDeviceObject, buffer, offset, setupBlockSize, &badSectorCount); - if (!NT_SUCCESS (status)) - { - SetupResult = status; - goto err; - } - } - else - { - SetupResult = status; - goto err; - } - } - - dataUnit.Value = offset.QuadPart / ENCRYPTION_DATA_UNIT_SIZE; - - if (SetupRequest.SetupMode == SetupEncryption) - { - EncryptDataUnits (buffer, &dataUnit, setupBlockSize / ENCRYPTION_DATA_UNIT_SIZE, Extension->Queue.CryptoInfo); - - if (SetupRequest.WipeAlgorithm != TC_WIPE_NONE) - { - byte wipePass; - int wipePassCount = GetWipePassCount (SetupRequest.WipeAlgorithm); - if (wipePassCount <= 0) - { - SetupResult = STATUS_INVALID_PARAMETER; - goto err; - } - - for (wipePass = 1; wipePass <= wipePassCount; ++wipePass) - { - if (!WipeBuffer (SetupRequest.WipeAlgorithm, wipeRandChars, wipePass, wipeBuffer, setupBlockSize)) - { - ULONG i; - for (i = 0; i < setupBlockSize; ++i) - { - wipeBuffer[i] = buffer[i] + wipePass; - } - - EncryptDataUnits (wipeBuffer, &dataUnit, setupBlockSize / ENCRYPTION_DATA_UNIT_SIZE, Extension->Queue.CryptoInfo); - memcpy (wipeRandCharsUpdate, wipeBuffer, sizeof (wipeRandCharsUpdate)); - } - - status = TCWriteDevice (BootDriveFilterExtension->LowerDeviceObject, wipeBuffer, offset, setupBlockSize); - if (!NT_SUCCESS (status)) - { - // Undo failed write operation - DecryptDataUnits (buffer, &dataUnit, setupBlockSize / ENCRYPTION_DATA_UNIT_SIZE, Extension->Queue.CryptoInfo); - TCWriteDevice (BootDriveFilterExtension->LowerDeviceObject, buffer, offset, setupBlockSize); - - SetupResult = status; - goto err; - } - } - - memcpy (wipeRandChars, wipeRandCharsUpdate, sizeof (wipeRandCharsUpdate)); - } - } - else - { - DecryptDataUnits (buffer, &dataUnit, setupBlockSize / ENCRYPTION_DATA_UNIT_SIZE, Extension->Queue.CryptoInfo); - } - - status = TCWriteDevice (BootDriveFilterExtension->LowerDeviceObject, buffer, offset, setupBlockSize); - if (!NT_SUCCESS (status)) - { - Dump ("TCWriteDevice error %x\n", status); - - // Undo failed write operation - if (SetupRequest.SetupMode == SetupEncryption) - DecryptDataUnits (buffer, &dataUnit, setupBlockSize / ENCRYPTION_DATA_UNIT_SIZE, Extension->Queue.CryptoInfo); - else - EncryptDataUnits (buffer, &dataUnit, setupBlockSize / ENCRYPTION_DATA_UNIT_SIZE, Extension->Queue.CryptoInfo); - - TCWriteDevice (BootDriveFilterExtension->LowerDeviceObject, buffer, offset, setupBlockSize); - - SetupResult = status; - goto err; - } - - if (SetupRequest.SetupMode == SetupEncryption) - offset.QuadPart += setupBlockSize; - - Extension->Queue.EncryptedAreaEndUpdatePending = TRUE; - Extension->Queue.EncryptedAreaEnd = offset.QuadPart - 1; - Extension->Queue.EncryptedAreaEndUpdatePending = FALSE; - - headerUpdateRequired = TRUE; - - EncryptedIoQueueResumeFromHold (&Extension->Queue); - - KeAcquireSpinLock (&SetupStatusSpinLock, &irql); - SetupStatusEncryptedAreaEnd = Extension->Queue.EncryptedAreaEnd; - KeReleaseSpinLock (&SetupStatusSpinLock, irql); - - // Update volume header - bytesWrittenSinceHeaderUpdate += setupBlockSize; - if (bytesWrittenSinceHeaderUpdate >= TC_ENCRYPTION_SETUP_HEADER_UPDATE_THRESHOLD) - { - status = SaveDriveVolumeHeader (Extension); - ASSERT (NT_SUCCESS (status)); - if (NT_SUCCESS (status)) - { - headerUpdateRequired = FALSE; - bytesWrittenSinceHeaderUpdate = 0; - } - } - } - -abort: - SetupResult = STATUS_SUCCESS; -err: - - if (Extension->Queue.EncryptedAreaEnd == -1) - Extension->Queue.EncryptedAreaStart = -1; - - if (EncryptedIoQueueIsSuspended (&Extension->Queue)) - EncryptedIoQueueResumeFromHold (&Extension->Queue); - - if (SetupRequest.SetupMode == SetupDecryption && Extension->Queue.EncryptedAreaStart >= Extension->Queue.EncryptedAreaEnd) - { - while (!NT_SUCCESS (EncryptedIoQueueHoldWhenIdle (&Extension->Queue, 0))); - - Extension->ConfiguredEncryptedAreaStart = Extension->ConfiguredEncryptedAreaEnd = -1; - Extension->Queue.EncryptedAreaStart = Extension->Queue.EncryptedAreaEnd = -1; - - EncryptedIoQueueResumeFromHold (&Extension->Queue); - - headerUpdateRequired = TRUE; - } - - Dump ("Setup completed: EncryptedAreaStart=%I64d (%I64d) EncryptedAreaEnd=%I64d (%I64d)\n", Extension->Queue.EncryptedAreaStart / 1024 / 1024, Extension->Queue.EncryptedAreaStart, Extension->Queue.EncryptedAreaEnd / 1024 / 1024, Extension->Queue.EncryptedAreaEnd); - - if (headerUpdateRequired) - { - status = SaveDriveVolumeHeader (Extension); - - if (!NT_SUCCESS (status) && NT_SUCCESS (SetupResult)) - SetupResult = status; - } - - if (SetupRequest.SetupMode == SetupDecryption && Extension->ConfiguredEncryptedAreaEnd == -1 && Extension->DriveMounted) - { - while (!RootDeviceControlMutexAcquireNoWait() && !EncryptionSetupThreadAbortRequested) - { - TCSleep (10); - } - - // Disable hibernation (resume would fail due to a change in the system memory map) - HibernationEnabled = FALSE; - - DismountDrive (Extension, FALSE); - - if (!EncryptionSetupThreadAbortRequested) - RootDeviceControlMutexRelease(); - } - -ret: - if (buffer) - { - burn (buffer, TC_ENCRYPTION_SETUP_IO_BLOCK_SIZE); - TCfree (buffer); - } - if (wipeBuffer) - { - burn (wipeBuffer, TC_ENCRYPTION_SETUP_IO_BLOCK_SIZE); - TCfree (wipeBuffer); - } - - burn (wipeRandChars, TC_WIPE_RAND_CHAR_COUNT); - burn (wipeRandCharsUpdate, TC_WIPE_RAND_CHAR_COUNT); - - SetupInProgress = FALSE; - PsTerminateSystemThread (SetupResult); -} - - -NTSTATUS StartBootEncryptionSetup (PDEVICE_OBJECT DeviceObject, PIRP irp, PIO_STACK_LOCATION irpSp) -{ - NTSTATUS status; - - if (!UserCanAccessDriveDevice()) - return STATUS_ACCESS_DENIED; - - if (SetupInProgress || !BootDriveFound || !BootDriveFilterExtension - || !BootDriveFilterExtension->DriveMounted - || BootDriveFilterExtension->HiddenSystem - || irpSp->Parameters.DeviceIoControl.InputBufferLength < sizeof (BootEncryptionSetupRequest)) - return STATUS_INVALID_PARAMETER; - - if (EncryptionSetupThread) - AbortBootEncryptionSetup(); - - SetupRequest = *(BootEncryptionSetupRequest *) irp->AssociatedIrp.SystemBuffer; - - EncryptionSetupThreadAbortRequested = FALSE; - KeInitializeSpinLock (&SetupStatusSpinLock); - SetupStatusEncryptedAreaEnd = BootDriveFilterExtension ? BootDriveFilterExtension->Queue.EncryptedAreaEnd : -1; - - SetupInProgress = TRUE; - status = TCStartThread (SetupThreadProc, DeviceObject, &EncryptionSetupThread); - - if (!NT_SUCCESS (status)) - SetupInProgress = FALSE; - - return status; -} - - -void GetBootDriveVolumeProperties (PIRP irp, PIO_STACK_LOCATION irpSp) -{ - if (ValidateIOBufferSize (irp, sizeof (VOLUME_PROPERTIES_STRUCT), ValidateOutput)) - { - DriveFilterExtension *Extension = BootDriveFilterExtension; - VOLUME_PROPERTIES_STRUCT *prop = (VOLUME_PROPERTIES_STRUCT *) irp->AssociatedIrp.SystemBuffer; - memset (prop, 0, sizeof (*prop)); - - if (!BootDriveFound || !Extension || !Extension->DriveMounted) - { - irp->IoStatus.Status = STATUS_INVALID_PARAMETER; - irp->IoStatus.Information = 0; - } - else - { - prop->hiddenVolume = Extension->Queue.CryptoInfo->hiddenVolume; - prop->diskLength = Extension->ConfiguredEncryptedAreaEnd + 1 - Extension->ConfiguredEncryptedAreaStart; - prop->ea = Extension->Queue.CryptoInfo->ea; - prop->mode = Extension->Queue.CryptoInfo->mode; - prop->pkcs5 = Extension->Queue.CryptoInfo->pkcs5; - prop->pkcs5Iterations = Extension->Queue.CryptoInfo->noIterations; - prop->volumePim = Extension->Queue.CryptoInfo->volumePim; -#if 0 - prop->volumeCreationTime = Extension->Queue.CryptoInfo->volume_creation_time; - prop->headerCreationTime = Extension->Queue.CryptoInfo->header_creation_time; -#endif - prop->volFormatVersion = Extension->Queue.CryptoInfo->LegacyVolume ? TC_VOLUME_FORMAT_VERSION_PRE_6_0 : TC_VOLUME_FORMAT_VERSION; - - prop->totalBytesRead = Extension->Queue.TotalBytesRead; - prop->totalBytesWritten = Extension->Queue.TotalBytesWritten; - - irp->IoStatus.Information = sizeof (VOLUME_PROPERTIES_STRUCT); - irp->IoStatus.Status = STATUS_SUCCESS; - } - } -} - - -void GetBootEncryptionStatus (PIRP irp, PIO_STACK_LOCATION irpSp) -{ - /* IMPORTANT: Do NOT add any potentially time-consuming operations to this function. */ - - if (ValidateIOBufferSize (irp, sizeof (BootEncryptionStatus), ValidateOutput)) - { - DriveFilterExtension *Extension = BootDriveFilterExtension; - BootEncryptionStatus *bootEncStatus = (BootEncryptionStatus *) irp->AssociatedIrp.SystemBuffer; - memset (bootEncStatus, 0, sizeof (*bootEncStatus)); - - if (BootArgsValid) - bootEncStatus->BootLoaderVersion = BootArgs.BootLoaderVersion; - - bootEncStatus->DeviceFilterActive = DeviceFilterActive; - bootEncStatus->SetupInProgress = SetupInProgress; - bootEncStatus->SetupMode = SetupRequest.SetupMode; - bootEncStatus->TransformWaitingForIdle = TransformWaitingForIdle; - - if (!BootDriveFound || !Extension || !Extension->DriveMounted) - { - bootEncStatus->DriveEncrypted = FALSE; - bootEncStatus->DriveMounted = FALSE; - bootEncStatus->VolumeHeaderPresent = FALSE; - } - else - { - bootEncStatus->DriveMounted = Extension->DriveMounted; - bootEncStatus->VolumeHeaderPresent = Extension->VolumeHeaderPresent; - bootEncStatus->DriveEncrypted = Extension->Queue.EncryptedAreaStart != -1; - bootEncStatus->BootDriveLength = BootDriveLength; - - bootEncStatus->ConfiguredEncryptedAreaStart = Extension->ConfiguredEncryptedAreaStart; - bootEncStatus->ConfiguredEncryptedAreaEnd = Extension->ConfiguredEncryptedAreaEnd; - bootEncStatus->EncryptedAreaStart = Extension->Queue.EncryptedAreaStart; - - if (SetupInProgress) - { - KIRQL irql; - KeAcquireSpinLock (&SetupStatusSpinLock, &irql); - bootEncStatus->EncryptedAreaEnd = SetupStatusEncryptedAreaEnd; - KeReleaseSpinLock (&SetupStatusSpinLock, irql); - } - else - bootEncStatus->EncryptedAreaEnd = Extension->Queue.EncryptedAreaEnd; - - bootEncStatus->VolumeHeaderSaltCrc32 = Extension->VolumeHeaderSaltCrc32; - bootEncStatus->HibernationPreventionCount = HibernationPreventionCount; - bootEncStatus->HiddenSysLeakProtectionCount = HiddenSysLeakProtectionCount; - - bootEncStatus->HiddenSystem = Extension->HiddenSystem; - - if (Extension->HiddenSystem) - bootEncStatus->HiddenSystemPartitionStart = BootArgs.HiddenSystemPartitionStart; - } - - irp->IoStatus.Information = sizeof (BootEncryptionStatus); - irp->IoStatus.Status = STATUS_SUCCESS; - } -} - - -void GetBootLoaderVersion (PIRP irp, PIO_STACK_LOCATION irpSp) -{ - if (ValidateIOBufferSize (irp, sizeof (uint16), ValidateOutput)) - { - if (BootArgsValid) - { - *(uint16 *) irp->AssociatedIrp.SystemBuffer = BootArgs.BootLoaderVersion; - irp->IoStatus.Information = sizeof (uint16); - irp->IoStatus.Status = STATUS_SUCCESS; - } - else - { - irp->IoStatus.Status = STATUS_INVALID_PARAMETER; - irp->IoStatus.Information = 0; - } - } -} - -void GetBootLoaderFingerprint (PIRP irp, PIO_STACK_LOCATION irpSp) -{ - if (ValidateIOBufferSize (irp, sizeof (BootLoaderFingerprintRequest), ValidateOutput)) - { - irp->IoStatus.Information = 0; - if (BootArgsValid && BootDriveFound && BootDriveFilterExtension && BootDriveFilterExtension->DriveMounted && BootDriveFilterExtension->HeaderCryptoInfo) - { - BootLoaderFingerprintRequest *bootLoaderFingerprint = (BootLoaderFingerprintRequest *) irp->AssociatedIrp.SystemBuffer; - - /* compute the fingerprint again and check if it is the same as the one retrieved during boot */ - char *header = TCalloc (TC_BOOT_ENCRYPTION_VOLUME_HEADER_SIZE); - if (!header) - { - irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES; - } - else - { - memcpy (bootLoaderFingerprint->Fingerprint, BootLoaderFingerprint, sizeof (BootLoaderFingerprint)); - ComputeBootLoaderFingerprint (BootDriveFilterExtension->LowerDeviceObject, header); - - burn (header, TC_BOOT_ENCRYPTION_VOLUME_HEADER_SIZE); - TCfree (header); - - if (0 == memcmp (bootLoaderFingerprint->Fingerprint, BootLoaderFingerprint, sizeof (BootLoaderFingerprint))) - { - irp->IoStatus.Information = sizeof (BootLoaderFingerprintRequest); - irp->IoStatus.Status = STATUS_SUCCESS; - } - else - { - /* fingerprint mismatch.*/ - irp->IoStatus.Status = STATUS_INVALID_IMAGE_HASH; - } - } - } - else - { - irp->IoStatus.Status = STATUS_INVALID_PARAMETER; - } - } -} - -void GetBootEncryptionAlgorithmName (PIRP irp, PIO_STACK_LOCATION irpSp) -{ - if (ValidateIOBufferSize (irp, sizeof (GetBootEncryptionAlgorithmNameRequest), ValidateOutput)) - { - if (BootDriveFilterExtension && BootDriveFilterExtension->DriveMounted) - { - wchar_t BootEncryptionAlgorithmNameW[256]; - wchar_t BootPrfAlgorithmNameW[256]; - GetBootEncryptionAlgorithmNameRequest *request = (GetBootEncryptionAlgorithmNameRequest *) irp->AssociatedIrp.SystemBuffer; - EAGetName (BootEncryptionAlgorithmNameW, BootDriveFilterExtension->Queue.CryptoInfo->ea, 0); - HashGetName2 (BootPrfAlgorithmNameW, BootDriveFilterExtension->Queue.CryptoInfo->pkcs5); - - RtlStringCbPrintfA (request->BootEncryptionAlgorithmName, sizeof (request->BootEncryptionAlgorithmName), "%S", BootEncryptionAlgorithmNameW); - RtlStringCbPrintfA (request->BootPrfAlgorithmName, sizeof (request->BootPrfAlgorithmName), "%S", BootPrfAlgorithmNameW); - - irp->IoStatus.Information = sizeof (GetBootEncryptionAlgorithmNameRequest); - irp->IoStatus.Status = STATUS_SUCCESS; - } - else - { - irp->IoStatus.Status = STATUS_INVALID_PARAMETER; - irp->IoStatus.Information = 0; - } - } -} - - -NTSTATUS GetSetupResult() -{ - return SetupResult; -} - - -BOOL IsBootDriveMounted () -{ - return BootDriveFilterExtension && BootDriveFilterExtension->DriveMounted; -} - - -BOOL IsBootEncryptionSetupInProgress () -{ - return SetupInProgress; -} - - -BOOL IsHiddenSystemRunning () -{ - return BootDriveFilterExtension && BootDriveFilterExtension->HiddenSystem; -} - - -DriveFilterExtension *GetBootDriveFilterExtension () -{ - return BootDriveFilterExtension; -} - - -CRYPTO_INFO *GetSystemDriveCryptoInfo () -{ - return BootDriveFilterExtension->Queue.CryptoInfo; -} - - -NTSTATUS AbortBootEncryptionSetup () -{ - if (!IoIsSystemThread (PsGetCurrentThread()) && !UserCanAccessDriveDevice()) - return STATUS_ACCESS_DENIED; - - if (EncryptionSetupThread) - { - EncryptionSetupThreadAbortRequested = TRUE; - - TCStopThread (EncryptionSetupThread, NULL); - EncryptionSetupThread = NULL; - } - - return STATUS_SUCCESS; -} - - -static VOID DecoySystemWipeThreadProc (PVOID threadArg) -{ - DriveFilterExtension *Extension = BootDriveFilterExtension; - - LARGE_INTEGER offset; - UINT64_STRUCT dataUnit; - ULONG wipeBlockSize = TC_ENCRYPTION_SETUP_IO_BLOCK_SIZE; - - CRYPTO_INFO *wipeCryptoInfo = NULL; - byte *wipeBuffer = NULL; - byte *wipeRandBuffer = NULL; - byte wipeRandChars[TC_WIPE_RAND_CHAR_COUNT]; - int wipePass, wipePassCount; - int ea = Extension->Queue.CryptoInfo->ea; - - KIRQL irql; - NTSTATUS status; - - DecoySystemWipeResult = STATUS_UNSUCCESSFUL; - - wipeBuffer = TCalloc (TC_ENCRYPTION_SETUP_IO_BLOCK_SIZE); - if (!wipeBuffer) - { - DecoySystemWipeResult = STATUS_INSUFFICIENT_RESOURCES; - goto ret; - } - - wipeRandBuffer = TCalloc (TC_ENCRYPTION_SETUP_IO_BLOCK_SIZE); - if (!wipeRandBuffer) - { - DecoySystemWipeResult = STATUS_INSUFFICIENT_RESOURCES; - goto ret; - } - - wipeCryptoInfo = crypto_open(); - if (!wipeCryptoInfo) - { - DecoySystemWipeResult = STATUS_INSUFFICIENT_RESOURCES; - goto ret; - } - - wipeCryptoInfo->ea = ea; - wipeCryptoInfo->mode = Extension->Queue.CryptoInfo->mode; - - if (EAInit (ea, WipeDecoyRequest.WipeKey, wipeCryptoInfo->ks) != ERR_SUCCESS) - { - DecoySystemWipeResult = STATUS_INVALID_PARAMETER; - goto ret; - } - - memcpy (wipeCryptoInfo->k2, WipeDecoyRequest.WipeKey + EAGetKeySize (ea), EAGetKeySize (ea)); - - if (!EAInitMode (wipeCryptoInfo)) - { - DecoySystemWipeResult = STATUS_INVALID_PARAMETER; - goto err; - } - - EncryptDataUnits (wipeRandBuffer, &dataUnit, wipeBlockSize / ENCRYPTION_DATA_UNIT_SIZE, wipeCryptoInfo); - memcpy (wipeRandChars, wipeRandBuffer, sizeof (wipeRandChars)); - - burn (WipeDecoyRequest.WipeKey, sizeof (WipeDecoyRequest.WipeKey)); - - offset.QuadPart = Extension->ConfiguredEncryptedAreaStart; - - Dump ("Wiping decoy system: start offset = %I64d\n", offset.QuadPart); - - while (!DecoySystemWipeThreadAbortRequested) - { - if (offset.QuadPart + wipeBlockSize > Extension->ConfiguredEncryptedAreaEnd + 1) - wipeBlockSize = (ULONG) (Extension->ConfiguredEncryptedAreaEnd + 1 - offset.QuadPart); - - if (offset.QuadPart > Extension->ConfiguredEncryptedAreaEnd) - break; - - wipePassCount = GetWipePassCount (WipeDecoyRequest.WipeAlgorithm); - if (wipePassCount <= 0) - { - DecoySystemWipeResult = STATUS_INVALID_PARAMETER; - goto err; - } - - for (wipePass = 1; wipePass <= wipePassCount; ++wipePass) - { - if (!WipeBuffer (WipeDecoyRequest.WipeAlgorithm, wipeRandChars, wipePass, wipeBuffer, wipeBlockSize)) - { - dataUnit.Value = offset.QuadPart / ENCRYPTION_DATA_UNIT_SIZE; - EncryptDataUnits (wipeRandBuffer, &dataUnit, wipeBlockSize / ENCRYPTION_DATA_UNIT_SIZE, wipeCryptoInfo); - memcpy (wipeBuffer, wipeRandBuffer, wipeBlockSize); - } - - while (!NT_SUCCESS (EncryptedIoQueueHoldWhenIdle (&Extension->Queue, 500))) - { - if (DecoySystemWipeThreadAbortRequested) - goto abort; - } - - status = TCWriteDevice (BootDriveFilterExtension->LowerDeviceObject, wipeBuffer, offset, wipeBlockSize); - - if (!NT_SUCCESS (status)) - { - DecoySystemWipeResult = status; - goto err; - } - - EncryptedIoQueueResumeFromHold (&Extension->Queue); - } - - offset.QuadPart += wipeBlockSize; - - KeAcquireSpinLock (&DecoySystemWipeStatusSpinLock, &irql); - DecoySystemWipedAreaEnd = offset.QuadPart - 1; - KeReleaseSpinLock (&DecoySystemWipeStatusSpinLock, irql); - } - -abort: - DecoySystemWipeResult = STATUS_SUCCESS; -err: - - if (EncryptedIoQueueIsSuspended (&Extension->Queue)) - EncryptedIoQueueResumeFromHold (&Extension->Queue); - - Dump ("Wipe end: DecoySystemWipedAreaEnd=%I64d (%I64d)\n", DecoySystemWipedAreaEnd, DecoySystemWipedAreaEnd / 1024 / 1024); - -ret: - if (wipeCryptoInfo) - crypto_close (wipeCryptoInfo); - - if (wipeRandBuffer) - TCfree (wipeRandBuffer); - - if (wipeBuffer) - TCfree (wipeBuffer); - - DecoySystemWipeInProgress = FALSE; - PsTerminateSystemThread (DecoySystemWipeResult); -} - - -NTSTATUS StartDecoySystemWipe (PDEVICE_OBJECT DeviceObject, PIRP irp, PIO_STACK_LOCATION irpSp) -{ - NTSTATUS status; - WipeDecoySystemRequest *request; - - if (!UserCanAccessDriveDevice()) - return STATUS_ACCESS_DENIED; - - if (!IsHiddenSystemRunning() - || irpSp->Parameters.DeviceIoControl.InputBufferLength < sizeof (WipeDecoySystemRequest)) - return STATUS_INVALID_PARAMETER; - - if (DecoySystemWipeInProgress) - return STATUS_SUCCESS; - - if (DecoySystemWipeThread) - AbortDecoySystemWipe(); - - request = (WipeDecoySystemRequest *) irp->AssociatedIrp.SystemBuffer; - WipeDecoyRequest = *request; - - burn (request->WipeKey, sizeof (request->WipeKey)); - - DecoySystemWipeThreadAbortRequested = FALSE; - KeInitializeSpinLock (&DecoySystemWipeStatusSpinLock); - DecoySystemWipedAreaEnd = BootDriveFilterExtension->ConfiguredEncryptedAreaStart; - - DecoySystemWipeInProgress = TRUE; - status = TCStartThread (DecoySystemWipeThreadProc, DeviceObject, &DecoySystemWipeThread); - - if (!NT_SUCCESS (status)) - DecoySystemWipeInProgress = FALSE; - - return status; -} - - -BOOL IsDecoySystemWipeInProgress() -{ - return DecoySystemWipeInProgress; -} - - -void GetDecoySystemWipeStatus (PIRP irp, PIO_STACK_LOCATION irpSp) -{ - if (ValidateIOBufferSize (irp, sizeof (DecoySystemWipeStatus), ValidateOutput)) - { - DecoySystemWipeStatus *wipeStatus = (DecoySystemWipeStatus *) irp->AssociatedIrp.SystemBuffer; - - if (!IsHiddenSystemRunning()) - { - irp->IoStatus.Status = STATUS_INVALID_PARAMETER; - irp->IoStatus.Information = 0; - } - else - { - wipeStatus->WipeInProgress = DecoySystemWipeInProgress; - wipeStatus->WipeAlgorithm = WipeDecoyRequest.WipeAlgorithm; - - if (DecoySystemWipeInProgress) - { - KIRQL irql; - KeAcquireSpinLock (&DecoySystemWipeStatusSpinLock, &irql); - wipeStatus->WipedAreaEnd = DecoySystemWipedAreaEnd; - KeReleaseSpinLock (&DecoySystemWipeStatusSpinLock, irql); - } - else - wipeStatus->WipedAreaEnd = DecoySystemWipedAreaEnd; - - irp->IoStatus.Information = sizeof (DecoySystemWipeStatus); - irp->IoStatus.Status = STATUS_SUCCESS; - } - } -} - - -NTSTATUS GetDecoySystemWipeResult() -{ - return DecoySystemWipeResult; -} - - -NTSTATUS AbortDecoySystemWipe () -{ - if (!IoIsSystemThread (PsGetCurrentThread()) && !UserCanAccessDriveDevice()) - return STATUS_ACCESS_DENIED; - - if (DecoySystemWipeThread) - { - DecoySystemWipeThreadAbortRequested = TRUE; - - TCStopThread (DecoySystemWipeThread, NULL); - DecoySystemWipeThread = NULL; - } - - return STATUS_SUCCESS; -} - - -uint64 GetBootDriveLength () -{ - return BootDriveLength.QuadPart; -} - - -NTSTATUS WriteBootDriveSector (PIRP irp, PIO_STACK_LOCATION irpSp) -{ - WriteBootDriveSectorRequest *request; - - if (!UserCanAccessDriveDevice()) - return STATUS_ACCESS_DENIED; - - if (!BootDriveFilterExtension - || irpSp->Parameters.DeviceIoControl.InputBufferLength < sizeof (WriteBootDriveSectorRequest)) - return STATUS_INVALID_PARAMETER; - - request = (WriteBootDriveSectorRequest *) irp->AssociatedIrp.SystemBuffer; - return TCWriteDevice (BootDriveFilterExtension->LowerDeviceObject, request->Data, request->Offset, sizeof (request->Data)); -} +/* + Derived from source code of TrueCrypt 7.1a, which is + Copyright (c) 2008-2012 TrueCrypt Developers Association and which is governed + by the TrueCrypt License 3.0. + + Modifications and additions to the original source code (contained in this file) + and all other portions of this file are Copyright (c) 2013-2016 IDRIX + and are governed by the Apache License 2.0 the full text of which is + contained in the file License.txt included in VeraCrypt binary and source + code distribution packages. +*/ + +#include "TCdefs.h" +#include +#include +#include +#include "Cache.h" +#include "Crc.h" +#include "Crypto.h" +#include "Apidrvr.h" +#include "EncryptedIoQueue.h" +#include "Common/Endian.h" +#include "Ntdriver.h" +#include "Ntvol.h" +#include "Volumes.h" +#include "VolumeFilter.h" +#include "Wipe.h" +#include "DriveFilter.h" +#include "Boot/Windows/BootCommon.h" + +static BOOL DeviceFilterActive = FALSE; + +BOOL BootArgsValid = FALSE; +BootArguments BootArgs; +static uint16 BootLoaderSegment; +static BOOL BootDriveSignatureValid = FALSE; + +static KMUTEX MountMutex; + +static volatile BOOL BootDriveFound = FALSE; +static DriveFilterExtension *BootDriveFilterExtension = NULL; +static LARGE_INTEGER BootDriveLength; +static byte BootLoaderFingerprint[WHIRLPOOL_DIGESTSIZE + SHA512_DIGESTSIZE]; + +static BOOL CrashDumpEnabled = FALSE; +static BOOL HibernationEnabled = FALSE; + +static BOOL LegacyHibernationDriverFilterActive = FALSE; +static byte *HibernationWriteBuffer = NULL; +static MDL *HibernationWriteBufferMdl = NULL; + +static uint32 HibernationPreventionCount = 0; + +static BootEncryptionSetupRequest SetupRequest; +static volatile BOOL SetupInProgress = FALSE; +PKTHREAD EncryptionSetupThread = NULL; +static volatile BOOL EncryptionSetupThreadAbortRequested; +static KSPIN_LOCK SetupStatusSpinLock; +static int64 SetupStatusEncryptedAreaEnd; +static BOOL TransformWaitingForIdle; +static NTSTATUS SetupResult; + +static WipeDecoySystemRequest WipeDecoyRequest; +static volatile BOOL DecoySystemWipeInProgress = FALSE; +static volatile BOOL DecoySystemWipeThreadAbortRequested; +static KSPIN_LOCK DecoySystemWipeStatusSpinLock; +static int64 DecoySystemWipedAreaEnd; +PKTHREAD DecoySystemWipeThread = NULL; +static NTSTATUS DecoySystemWipeResult; + + +NTSTATUS LoadBootArguments () +{ + NTSTATUS status = STATUS_UNSUCCESSFUL; + PHYSICAL_ADDRESS bootArgsAddr; + byte *mappedBootArgs; + uint16 bootLoaderSegment; + + KeInitializeMutex (&MountMutex, 0); + + for (bootLoaderSegment = TC_BOOT_LOADER_SEGMENT; + bootLoaderSegment >= TC_BOOT_LOADER_SEGMENT - 64 * 1024 / 16 && status != STATUS_SUCCESS; + bootLoaderSegment -= 32 * 1024 / 16) + { + bootArgsAddr.QuadPart = (bootLoaderSegment << 4) + TC_BOOT_LOADER_ARGS_OFFSET; + Dump ("Checking BootArguments at 0x%x\n", bootArgsAddr.LowPart); + + mappedBootArgs = MmMapIoSpace (bootArgsAddr, sizeof (BootArguments), MmCached); + if (!mappedBootArgs) + return STATUS_INSUFFICIENT_RESOURCES; + + if (TC_IS_BOOT_ARGUMENTS_SIGNATURE (mappedBootArgs)) + { + BootArguments *bootArguments = (BootArguments *) mappedBootArgs; + Dump ("BootArguments found at 0x%x\n", bootArgsAddr.LowPart); + + DumpMem (mappedBootArgs, sizeof (BootArguments)); + + if (bootArguments->BootLoaderVersion == VERSION_NUM + && bootArguments->BootArgumentsCrc32 != GetCrc32 ((byte *) bootArguments, (int) ((byte *) &bootArguments->BootArgumentsCrc32 - (byte *) bootArguments))) + { + Dump ("BootArguments CRC incorrect\n"); + TC_BUG_CHECK (STATUS_CRC_ERROR); + } + + // Sanity check: for valid boot argument, the password is less than 64 bytes long + if (bootArguments->BootPassword.Length <= MAX_PASSWORD) + { + BootLoaderSegment = bootLoaderSegment; + + BootArgs = *bootArguments; + BootArgsValid = TRUE; + burn (bootArguments, sizeof (*bootArguments)); + + BootDriveSignatureValid = TRUE; + + Dump ("BootLoaderVersion = %x\n", (int) BootArgs.BootLoaderVersion); + Dump ("HeaderSaltCrc32 = %x\n", (int) BootArgs.HeaderSaltCrc32); + Dump ("CryptoInfoOffset = %x\n", (int) BootArgs.CryptoInfoOffset); + Dump ("CryptoInfoLength = %d\n", (int) BootArgs.CryptoInfoLength); + Dump ("HiddenSystemPartitionStart = %I64u\n", BootArgs.HiddenSystemPartitionStart); + Dump ("DecoySystemPartitionStart = %I64u\n", BootArgs.DecoySystemPartitionStart); + Dump ("Flags = %x\n", BootArgs.Flags); + Dump ("BootDriveSignature = %x\n", BootArgs.BootDriveSignature); + Dump ("BootArgumentsCrc32 = %x\n", BootArgs.BootArgumentsCrc32); + + if (CacheBootPassword && BootArgs.BootPassword.Length > 0) + { + int pim = CacheBootPim? (int) (BootArgs.Flags >> 16) : 0; + AddPasswordToCache (&BootArgs.BootPassword, pim); + } + + // clear fingerprint + burn (BootLoaderFingerprint, sizeof (BootLoaderFingerprint)); + + status = STATUS_SUCCESS; + } + } + + MmUnmapIoSpace (mappedBootArgs, sizeof (BootArguments)); + } + + return status; +} + + +NTSTATUS DriveFilterAddDevice (PDRIVER_OBJECT driverObject, PDEVICE_OBJECT pdo) +{ + DriveFilterExtension *Extension; + NTSTATUS status; + PDEVICE_OBJECT filterDeviceObject = NULL; + PDEVICE_OBJECT attachedDeviceObject; + + Dump ("DriveFilterAddDevice pdo=%p\n", pdo); + + attachedDeviceObject = IoGetAttachedDeviceReference (pdo); + status = IoCreateDevice (driverObject, sizeof (DriveFilterExtension), NULL, attachedDeviceObject->DeviceType, 0, FALSE, &filterDeviceObject); + + ObDereferenceObject (attachedDeviceObject); + + if (!NT_SUCCESS (status)) + { + filterDeviceObject = NULL; + goto err; + } + + Extension = (DriveFilterExtension *) filterDeviceObject->DeviceExtension; + memset (Extension, 0, sizeof (DriveFilterExtension)); + + status = IoAttachDeviceToDeviceStackSafe (filterDeviceObject, pdo, &(Extension->LowerDeviceObject)); + if (!NT_SUCCESS (status)) + { + goto err; + } + + if (!Extension->LowerDeviceObject) + { + status = STATUS_DEVICE_REMOVED; + goto err; + } + + Extension->IsDriveFilterDevice = Extension->Queue.IsFilterDevice = TRUE; + Extension->DeviceObject = Extension->Queue.DeviceObject = filterDeviceObject; + Extension->Pdo = pdo; + + Extension->Queue.LowerDeviceObject = Extension->LowerDeviceObject; + IoInitializeRemoveLock (&Extension->Queue.RemoveLock, 'LRCV', 0, 0); + + Extension->ConfiguredEncryptedAreaStart = -1; + Extension->ConfiguredEncryptedAreaEnd = -1; + Extension->Queue.EncryptedAreaStart = -1; + Extension->Queue.EncryptedAreaEnd = -1; + Extension->Queue.EncryptedAreaEndUpdatePending = FALSE; + + filterDeviceObject->Flags |= Extension->LowerDeviceObject->Flags & (DO_DIRECT_IO | DO_BUFFERED_IO | DO_POWER_PAGABLE); + filterDeviceObject->Flags &= ~DO_DEVICE_INITIALIZING; + + DeviceFilterActive = TRUE; + return status; + +err: + if (filterDeviceObject) + { + if (Extension->LowerDeviceObject) + IoDetachDevice (Extension->LowerDeviceObject); + + IoDeleteDevice (filterDeviceObject); + } + + return status; +} + + +static void DismountDrive (DriveFilterExtension *Extension, BOOL stopIoQueue) +{ + Dump ("Dismounting drive\n"); + ASSERT (Extension->DriveMounted); + + if (stopIoQueue && EncryptedIoQueueIsRunning (&Extension->Queue)) + EncryptedIoQueueStop (&Extension->Queue); + + crypto_close (Extension->Queue.CryptoInfo); + Extension->Queue.CryptoInfo = NULL; + + crypto_close (Extension->HeaderCryptoInfo); + Extension->HeaderCryptoInfo = NULL; + + Extension->DriveMounted = FALSE; +} + +static void ComputeBootLoaderFingerprint(PDEVICE_OBJECT LowerDeviceObject, byte* ioBuffer /* ioBuffer must be at least 512 bytes long */) +{ + NTSTATUS status; + LARGE_INTEGER offset; + WHIRLPOOL_CTX whirlpool; + sha512_ctx sha2; + ULONG bytesToRead, remainingBytes, bootloaderTotalSize = TC_BOOT_LOADER_AREA_SIZE - TC_BOOT_ENCRYPTION_VOLUME_HEADER_SIZE; + + // clear fingerprint + burn (BootLoaderFingerprint, sizeof (BootLoaderFingerprint)); + + // compute Whirlpool+SHA512 fingerprint of bootloader including MBR + // we skip user configuration fields: + // TC_BOOT_SECTOR_PIM_VALUE_OFFSET = 400 + // TC_BOOT_SECTOR_OUTER_VOLUME_BAK_HEADER_CRC_OFFSET = 402 + // => TC_BOOT_SECTOR_OUTER_VOLUME_BAK_HEADER_CRC_SIZE = 4 + // TC_BOOT_SECTOR_USER_MESSAGE_OFFSET = 406 + // => TC_BOOT_SECTOR_USER_MESSAGE_MAX_LENGTH = 24 + // TC_BOOT_SECTOR_USER_CONFIG_OFFSET = 438 + // + // we have: TC_BOOT_SECTOR_USER_MESSAGE_OFFSET = TC_BOOT_SECTOR_OUTER_VOLUME_BAK_HEADER_CRC_OFFSET + TC_BOOT_SECTOR_OUTER_VOLUME_BAK_HEADER_CRC_SIZE + + WHIRLPOOL_init (&whirlpool); + sha512_begin (&sha2); + // read the first 512 bytes + offset.QuadPart = 0; + + status = TCReadDevice (LowerDeviceObject, ioBuffer, offset, TC_SECTOR_SIZE_BIOS); + if (NT_SUCCESS (status)) + { + WHIRLPOOL_add (ioBuffer, TC_BOOT_SECTOR_PIM_VALUE_OFFSET * 8, &whirlpool); + WHIRLPOOL_add (ioBuffer + TC_BOOT_SECTOR_USER_MESSAGE_OFFSET + TC_BOOT_SECTOR_USER_MESSAGE_MAX_LENGTH, (TC_BOOT_SECTOR_USER_CONFIG_OFFSET - (TC_BOOT_SECTOR_USER_MESSAGE_OFFSET + TC_BOOT_SECTOR_USER_MESSAGE_MAX_LENGTH)) * 8, &whirlpool); + WHIRLPOOL_add (ioBuffer + TC_BOOT_SECTOR_USER_CONFIG_OFFSET + 1, (TC_MAX_MBR_BOOT_CODE_SIZE - (TC_BOOT_SECTOR_USER_CONFIG_OFFSET + 1)) * 8, &whirlpool); + + sha512_hash (ioBuffer, TC_BOOT_SECTOR_PIM_VALUE_OFFSET, &sha2); + sha512_hash (ioBuffer + TC_BOOT_SECTOR_USER_MESSAGE_OFFSET + TC_BOOT_SECTOR_USER_MESSAGE_MAX_LENGTH, (TC_BOOT_SECTOR_USER_CONFIG_OFFSET - (TC_BOOT_SECTOR_USER_MESSAGE_OFFSET + TC_BOOT_SECTOR_USER_MESSAGE_MAX_LENGTH)), &sha2); + sha512_hash (ioBuffer + TC_BOOT_SECTOR_USER_CONFIG_OFFSET + 1, (TC_MAX_MBR_BOOT_CODE_SIZE - (TC_BOOT_SECTOR_USER_CONFIG_OFFSET + 1)), &sha2); + + // we has the reste of the bootloader, 512 bytes at a time + offset.QuadPart = TC_SECTOR_SIZE_BIOS; + remainingBytes = bootloaderTotalSize - TC_SECTOR_SIZE_BIOS; + + while (NT_SUCCESS (status) && (remainingBytes > 0)) + { + bytesToRead = (remainingBytes >= TC_SECTOR_SIZE_BIOS)? TC_SECTOR_SIZE_BIOS : remainingBytes; + status = TCReadDevice (LowerDeviceObject, ioBuffer, offset, bytesToRead); + if (NT_SUCCESS (status)) + { + remainingBytes -= bytesToRead; + offset.QuadPart += bytesToRead; + WHIRLPOOL_add (ioBuffer, bytesToRead * 8, &whirlpool); + sha512_hash (ioBuffer, bytesToRead, &sha2); + } + else + { + Dump ("TCReadDevice error %x during ComputeBootLoaderFingerprint call\n", status); + break; + } + } + + if (NT_SUCCESS (status)) + { + WHIRLPOOL_finalize (&whirlpool, BootLoaderFingerprint); + sha512_end (&BootLoaderFingerprint [WHIRLPOOL_DIGESTSIZE], &sha2); + } + } + else + { + Dump ("TCReadDevice error %x during ComputeBootLoaderFingerprint call\n", status); + } +} + + +static NTSTATUS MountDrive (DriveFilterExtension *Extension, Password *password, uint32 *headerSaltCrc32) +{ + BOOL hiddenVolume = (BootArgs.HiddenSystemPartitionStart != 0); + int64 hiddenHeaderOffset = BootArgs.HiddenSystemPartitionStart + TC_HIDDEN_VOLUME_HEADER_OFFSET; + NTSTATUS status; + LARGE_INTEGER offset; + char *header; + int pkcs5_prf = 0, pim = 0; + byte *mappedCryptoInfo = NULL; + + Dump ("MountDrive pdo=%p\n", Extension->Pdo); + ASSERT (KeGetCurrentIrql() == PASSIVE_LEVEL); + + // Check boot drive signature first (header CRC search could fail if a user restored the header to a non-boot drive) + if (BootDriveSignatureValid) + { + byte mbr[TC_SECTOR_SIZE_BIOS]; + + offset.QuadPart = 0; + status = TCReadDevice (Extension->LowerDeviceObject, mbr, offset, TC_SECTOR_SIZE_BIOS); + + if (NT_SUCCESS (status) && BootArgs.BootDriveSignature != *(uint32 *) (mbr + 0x1b8)) + return STATUS_UNSUCCESSFUL; + } + + header = TCalloc (TC_BOOT_ENCRYPTION_VOLUME_HEADER_SIZE); + if (!header) + return STATUS_INSUFFICIENT_RESOURCES; + + offset.QuadPart = hiddenVolume ? hiddenHeaderOffset : TC_BOOT_VOLUME_HEADER_SECTOR_OFFSET; + Dump ("Reading volume header at %I64u\n", offset.QuadPart); + + status = TCReadDevice (Extension->LowerDeviceObject, header, offset, TC_BOOT_ENCRYPTION_VOLUME_HEADER_SIZE); + if (!NT_SUCCESS (status)) + { + Dump ("TCReadDevice error %x\n", status); + goto ret; + } + + if (headerSaltCrc32) + { + uint32 saltCrc = GetCrc32 (header, PKCS5_SALT_SIZE); + + if (saltCrc != *headerSaltCrc32) + { + status = STATUS_UNSUCCESSFUL; + goto ret; + } + + Extension->VolumeHeaderSaltCrc32 = saltCrc; + } + + Extension->HeaderCryptoInfo = crypto_open(); + if (!Extension->HeaderCryptoInfo) + { + status = STATUS_INSUFFICIENT_RESOURCES; + goto ret; + } + + if (BootArgs.CryptoInfoLength > 0) + { + PHYSICAL_ADDRESS cryptoInfoAddress; + + cryptoInfoAddress.QuadPart = (BootLoaderSegment << 4) + BootArgs.CryptoInfoOffset; +#ifdef DEBUG + Dump ("Wiping memory %x %d\n", cryptoInfoAddress.LowPart, BootArgs.CryptoInfoLength); +#endif + mappedCryptoInfo = MmMapIoSpace (cryptoInfoAddress, BootArgs.CryptoInfoLength, MmCached); + if (mappedCryptoInfo) + { + /* Get the parameters used for booting to speed up driver startup and avoid testing irrelevant PRFs */ + BOOT_CRYPTO_HEADER* pBootCryptoInfo = (BOOT_CRYPTO_HEADER*) mappedCryptoInfo; + Hash* pHash = HashGet(pBootCryptoInfo->pkcs5); + if (pHash && pHash->SystemEncryption) + pkcs5_prf = pBootCryptoInfo->pkcs5; + } + } + + pim = (int) (BootArgs.Flags >> 16); + + if (ReadVolumeHeader (!hiddenVolume, header, password, pkcs5_prf, pim, FALSE, &Extension->Queue.CryptoInfo, Extension->HeaderCryptoInfo) == 0) + { + // Header decrypted + status = STATUS_SUCCESS; + Dump ("Header decrypted\n"); + + // calculate Fingerprint + ComputeBootLoaderFingerprint (Extension->LowerDeviceObject, header); + + if (Extension->Queue.CryptoInfo->hiddenVolume) + { + int64 hiddenPartitionOffset = BootArgs.HiddenSystemPartitionStart; + Dump ("Hidden volume start offset = %I64d\n", Extension->Queue.CryptoInfo->EncryptedAreaStart.Value + hiddenPartitionOffset); + + Extension->HiddenSystem = TRUE; + + Extension->Queue.RemapEncryptedArea = TRUE; + Extension->Queue.RemappedAreaOffset = hiddenPartitionOffset + Extension->Queue.CryptoInfo->EncryptedAreaStart.Value - BootArgs.DecoySystemPartitionStart; + Extension->Queue.RemappedAreaDataUnitOffset = Extension->Queue.CryptoInfo->EncryptedAreaStart.Value / ENCRYPTION_DATA_UNIT_SIZE - BootArgs.DecoySystemPartitionStart / ENCRYPTION_DATA_UNIT_SIZE; + + Extension->Queue.CryptoInfo->EncryptedAreaStart.Value = BootArgs.DecoySystemPartitionStart; + + if (Extension->Queue.CryptoInfo->VolumeSize.Value > hiddenPartitionOffset - BootArgs.DecoySystemPartitionStart) + TC_THROW_FATAL_EXCEPTION; + + Dump ("RemappedAreaOffset = %I64d\n", Extension->Queue.RemappedAreaOffset); + Dump ("RemappedAreaDataUnitOffset = %I64d\n", Extension->Queue.RemappedAreaDataUnitOffset); + } + else + { + Extension->HiddenSystem = FALSE; + Extension->Queue.RemapEncryptedArea = FALSE; + } + + Extension->ConfiguredEncryptedAreaStart = Extension->Queue.CryptoInfo->EncryptedAreaStart.Value; + Extension->ConfiguredEncryptedAreaEnd = Extension->Queue.CryptoInfo->EncryptedAreaStart.Value + Extension->Queue.CryptoInfo->VolumeSize.Value - 1; + + Extension->Queue.EncryptedAreaStart = Extension->Queue.CryptoInfo->EncryptedAreaStart.Value; + Extension->Queue.EncryptedAreaEnd = Extension->Queue.CryptoInfo->EncryptedAreaStart.Value + Extension->Queue.CryptoInfo->EncryptedAreaLength.Value - 1; + + if (Extension->Queue.CryptoInfo->EncryptedAreaLength.Value == 0) + { + Extension->Queue.EncryptedAreaStart = -1; + Extension->Queue.EncryptedAreaEnd = -1; + } + + Dump ("Loaded: ConfiguredEncryptedAreaStart=%I64d (%I64d) ConfiguredEncryptedAreaEnd=%I64d (%I64d)\n", Extension->ConfiguredEncryptedAreaStart / 1024 / 1024, Extension->ConfiguredEncryptedAreaStart, Extension->ConfiguredEncryptedAreaEnd / 1024 / 1024, Extension->ConfiguredEncryptedAreaEnd); + Dump ("Loaded: EncryptedAreaStart=%I64d (%I64d) EncryptedAreaEnd=%I64d (%I64d)\n", Extension->Queue.EncryptedAreaStart / 1024 / 1024, Extension->Queue.EncryptedAreaStart, Extension->Queue.EncryptedAreaEnd / 1024 / 1024, Extension->Queue.EncryptedAreaEnd); + + // Erase boot loader scheduled keys + if (mappedCryptoInfo) + { + burn (mappedCryptoInfo, BootArgs.CryptoInfoLength); + MmUnmapIoSpace (mappedCryptoInfo, BootArgs.CryptoInfoLength); + BootArgs.CryptoInfoLength = 0; + } + + BootDriveFilterExtension = Extension; + BootDriveFound = Extension->BootDrive = Extension->DriveMounted = Extension->VolumeHeaderPresent = TRUE; + BootDriveFilterExtension->MagicNumber = TC_BOOT_DRIVE_FILTER_EXTENSION_MAGIC_NUMBER; + + burn (&BootArgs.BootPassword, sizeof (BootArgs.BootPassword)); + + { + STORAGE_DEVICE_NUMBER storageDeviceNumber; + status = SendDeviceIoControlRequest (Extension->LowerDeviceObject, IOCTL_STORAGE_GET_DEVICE_NUMBER, NULL, 0, &storageDeviceNumber, sizeof (storageDeviceNumber)); + + if (!NT_SUCCESS (status)) + { + Dump ("Failed to get drive number - error %x\n", status); + Extension->SystemStorageDeviceNumberValid = FALSE; + } + else + { + Extension->SystemStorageDeviceNumber = storageDeviceNumber.DeviceNumber; + Extension->SystemStorageDeviceNumberValid = TRUE; + } + } + + status = SendDeviceIoControlRequest (Extension->LowerDeviceObject, IOCTL_DISK_GET_LENGTH_INFO, NULL, 0, &BootDriveLength, sizeof (BootDriveLength)); + + if (!NT_SUCCESS (status)) + { + Dump ("Failed to get drive length - error %x\n", status); + BootDriveLength.QuadPart = 0; + Extension->Queue.MaxReadAheadOffset.QuadPart = 0; + } + else + Extension->Queue.MaxReadAheadOffset = BootDriveLength; + + status = EncryptedIoQueueStart (&Extension->Queue); + if (!NT_SUCCESS (status)) + TC_BUG_CHECK (status); + + if (IsOSAtLeast (WIN_VISTA)) + { + CrashDumpEnabled = TRUE; + HibernationEnabled = TRUE; + } + else if (!LegacyHibernationDriverFilterActive) + StartLegacyHibernationDriverFilter(); + + // Hidden system hibernation is not supported if an extra boot partition is present as the system is not allowed to update the boot partition + if (IsHiddenSystemRunning() && (BootArgs.Flags & TC_BOOT_ARGS_FLAG_EXTRA_BOOT_PARTITION)) + { + CrashDumpEnabled = FALSE; + HibernationEnabled = FALSE; + } + } + else + { + Dump ("Header not decrypted\n"); + crypto_close (Extension->HeaderCryptoInfo); + Extension->HeaderCryptoInfo = NULL; + + status = STATUS_UNSUCCESSFUL; + } + +ret: + TCfree (header); + return status; +} + + +static NTSTATUS SaveDriveVolumeHeader (DriveFilterExtension *Extension) +{ + NTSTATUS status = STATUS_SUCCESS; + LARGE_INTEGER offset; + byte *header; + + header = TCalloc (TC_BOOT_ENCRYPTION_VOLUME_HEADER_SIZE); + if (!header) + return STATUS_INSUFFICIENT_RESOURCES; + + offset.QuadPart = TC_BOOT_VOLUME_HEADER_SECTOR_OFFSET; + + status = TCReadDevice (Extension->LowerDeviceObject, header, offset, TC_BOOT_ENCRYPTION_VOLUME_HEADER_SIZE); + if (!NT_SUCCESS (status)) + { + Dump ("TCReadDevice error %x", status); + goto ret; + } + + Dump ("Saving: ConfiguredEncryptedAreaStart=%I64d (%I64d) ConfiguredEncryptedAreaEnd=%I64d (%I64d)\n", Extension->ConfiguredEncryptedAreaStart / 1024 / 1024, Extension->ConfiguredEncryptedAreaStart, Extension->ConfiguredEncryptedAreaEnd / 1024 / 1024, Extension->ConfiguredEncryptedAreaEnd); + Dump ("Saving: EncryptedAreaStart=%I64d (%I64d) EncryptedAreaEnd=%I64d (%I64d)\n", Extension->Queue.EncryptedAreaStart / 1024 / 1024, Extension->Queue.EncryptedAreaStart, Extension->Queue.EncryptedAreaEnd / 1024 / 1024, Extension->Queue.EncryptedAreaEnd); + + if (Extension->Queue.EncryptedAreaStart == -1 || Extension->Queue.EncryptedAreaEnd == -1 + || Extension->Queue.EncryptedAreaEnd <= Extension->Queue.EncryptedAreaStart) + { + if (SetupRequest.SetupMode == SetupDecryption) + { + memset (header, 0, TC_BOOT_ENCRYPTION_VOLUME_HEADER_SIZE); + Extension->VolumeHeaderPresent = FALSE; + } + } + else + { + uint32 headerCrc32; + uint64 encryptedAreaLength = Extension->Queue.EncryptedAreaEnd + 1 - Extension->Queue.EncryptedAreaStart; + byte *fieldPos = header + TC_HEADER_OFFSET_ENCRYPTED_AREA_LENGTH; + + DecryptBuffer (header + HEADER_ENCRYPTED_DATA_OFFSET, HEADER_ENCRYPTED_DATA_SIZE, Extension->HeaderCryptoInfo); + + if (GetHeaderField32 (header, TC_HEADER_OFFSET_MAGIC) != 0x56455241) + { + Dump ("Header not decrypted"); + status = STATUS_UNKNOWN_REVISION; + goto ret; + } + + mputInt64 (fieldPos, encryptedAreaLength); + + headerCrc32 = GetCrc32 (header + TC_HEADER_OFFSET_MAGIC, TC_HEADER_OFFSET_HEADER_CRC - TC_HEADER_OFFSET_MAGIC); + fieldPos = header + TC_HEADER_OFFSET_HEADER_CRC; + mputLong (fieldPos, headerCrc32); + + EncryptBuffer (header + HEADER_ENCRYPTED_DATA_OFFSET, HEADER_ENCRYPTED_DATA_SIZE, Extension->HeaderCryptoInfo); + } + + status = TCWriteDevice (Extension->LowerDeviceObject, header, offset, TC_BOOT_ENCRYPTION_VOLUME_HEADER_SIZE); + if (!NT_SUCCESS (status)) + { + Dump ("TCWriteDevice error %x", status); + goto ret; + } + +ret: + TCfree (header); + return status; +} + + +static NTSTATUS PassIrp (PDEVICE_OBJECT deviceObject, PIRP irp) +{ + IoSkipCurrentIrpStackLocation (irp); + return IoCallDriver (deviceObject, irp); +} + + +static NTSTATUS PassFilteredIrp (PDEVICE_OBJECT deviceObject, PIRP irp, PIO_COMPLETION_ROUTINE completionRoutine, PVOID completionRoutineArg) +{ + IoCopyCurrentIrpStackLocationToNext (irp); + + if (completionRoutine) + IoSetCompletionRoutine (irp, completionRoutine, completionRoutineArg, TRUE, TRUE, TRUE); + + return IoCallDriver (deviceObject, irp); +} + + +static NTSTATUS OnDeviceUsageNotificationCompleted (PDEVICE_OBJECT filterDeviceObject, PIRP Irp, DriveFilterExtension *Extension) +{ + if (Irp->PendingReturned) + IoMarkIrpPending (Irp); + + if (!(Extension->LowerDeviceObject->Flags & DO_POWER_PAGABLE)) + filterDeviceObject->Flags &= ~DO_POWER_PAGABLE; + + IoReleaseRemoveLock (&Extension->Queue.RemoveLock, Irp); + return STATUS_CONTINUE_COMPLETION; +} + + +static BOOL IsVolumeDevice (PDEVICE_OBJECT deviceObject) +{ + VOLUME_NUMBER volNumber; + VOLUME_DISK_EXTENTS extents[2]; + NTSTATUS extentStatus = SendDeviceIoControlRequest (deviceObject, IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS, NULL, 0, extents, sizeof (extents)); + + return NT_SUCCESS (SendDeviceIoControlRequest (deviceObject, IOCTL_VOLUME_SUPPORTS_ONLINE_OFFLINE, NULL, 0, NULL, 0)) + || NT_SUCCESS (SendDeviceIoControlRequest (deviceObject, IOCTL_VOLUME_IS_OFFLINE, NULL, 0, NULL, 0)) + || NT_SUCCESS (SendDeviceIoControlRequest (deviceObject, IOCTL_VOLUME_IS_IO_CAPABLE, NULL, 0, NULL, 0)) + || NT_SUCCESS (SendDeviceIoControlRequest (deviceObject, IOCTL_VOLUME_IS_PARTITION, NULL, 0, NULL, 0)) + || NT_SUCCESS (SendDeviceIoControlRequest (deviceObject, IOCTL_VOLUME_QUERY_VOLUME_NUMBER, NULL, 0, &volNumber, sizeof (volNumber))) + || NT_SUCCESS (extentStatus) || extentStatus == STATUS_BUFFER_OVERFLOW || extentStatus == STATUS_BUFFER_TOO_SMALL; +} + + +static void CheckDeviceTypeAndMount (DriveFilterExtension *filterExtension) +{ + if (BootArgsValid) + { + // Windows sometimes merges a removable drive PDO and its volume PDO to a single PDO having no volume interface (GUID_DEVINTERFACE_VOLUME). + // Therefore, we need to test whether the device supports volume IOCTLs. + if (VolumeClassFilterRegistered + && BootArgs.HiddenSystemPartitionStart != 0 + && IsVolumeDevice (filterExtension->LowerDeviceObject)) + { + Dump ("Drive and volume merged pdo=%p", filterExtension->Pdo); + + filterExtension->IsVolumeFilterDevice = TRUE; + filterExtension->IsDriveFilterDevice = FALSE; + } + else + { + NTSTATUS status = KeWaitForMutexObject (&MountMutex, Executive, KernelMode, FALSE, NULL); + if (!NT_SUCCESS (status)) + TC_BUG_CHECK (status); + + if (!BootDriveFound) + MountDrive (filterExtension, &BootArgs.BootPassword, &BootArgs.HeaderSaltCrc32); + + KeReleaseMutex (&MountMutex, FALSE); + } + } +} + + +static VOID MountDriveWorkItemRoutine (PDEVICE_OBJECT deviceObject, DriveFilterExtension *filterExtension) +{ + CheckDeviceTypeAndMount (filterExtension); + KeSetEvent (&filterExtension->MountWorkItemCompletedEvent, IO_NO_INCREMENT, FALSE); +} + + +static NTSTATUS OnStartDeviceCompleted (PDEVICE_OBJECT filterDeviceObject, PIRP Irp, DriveFilterExtension *Extension) +{ + if (Irp->PendingReturned) + IoMarkIrpPending (Irp); + + if (Extension->LowerDeviceObject->Characteristics & FILE_REMOVABLE_MEDIA) + filterDeviceObject->Characteristics |= FILE_REMOVABLE_MEDIA; + + if (KeGetCurrentIrql() == PASSIVE_LEVEL) + { + CheckDeviceTypeAndMount (Extension); + } + else + { + PIO_WORKITEM workItem = IoAllocateWorkItem (filterDeviceObject); + if (!workItem) + { + IoReleaseRemoveLock (&Extension->Queue.RemoveLock, Irp); + return STATUS_INSUFFICIENT_RESOURCES; + } + + KeInitializeEvent (&Extension->MountWorkItemCompletedEvent, SynchronizationEvent, FALSE); + IoQueueWorkItem (workItem, MountDriveWorkItemRoutine, DelayedWorkQueue, Extension); + + KeWaitForSingleObject (&Extension->MountWorkItemCompletedEvent, Executive, KernelMode, FALSE, NULL); + IoFreeWorkItem (workItem); + } + + IoReleaseRemoveLock (&Extension->Queue.RemoveLock, Irp); + return STATUS_CONTINUE_COMPLETION; +} + + +static NTSTATUS DispatchPnp (PDEVICE_OBJECT DeviceObject, PIRP Irp, DriveFilterExtension *Extension, PIO_STACK_LOCATION irpSp) +{ + NTSTATUS status; + + status = IoAcquireRemoveLock (&Extension->Queue.RemoveLock, Irp); + if (!NT_SUCCESS (status)) + return TCCompleteIrp (Irp, status, 0); + + switch (irpSp->MinorFunction) + { + case IRP_MN_START_DEVICE: + Dump ("IRP_MN_START_DEVICE pdo=%p\n", Extension->Pdo); + return PassFilteredIrp (Extension->LowerDeviceObject, Irp, OnStartDeviceCompleted, Extension); + + + case IRP_MN_DEVICE_USAGE_NOTIFICATION: + Dump ("IRP_MN_DEVICE_USAGE_NOTIFICATION type=%d\n", (int) irpSp->Parameters.UsageNotification.Type); + + { + PDEVICE_OBJECT attachedDevice = IoGetAttachedDeviceReference (DeviceObject); + + if (attachedDevice == DeviceObject || (attachedDevice->Flags & DO_POWER_PAGABLE)) + DeviceObject->Flags |= DO_POWER_PAGABLE; + + ObDereferenceObject (attachedDevice); + } + + // Prevent creation of hibernation and crash dump files if required + if (irpSp->Parameters.UsageNotification.InPath + && ( + (irpSp->Parameters.UsageNotification.Type == DeviceUsageTypeDumpFile && !CrashDumpEnabled) + || (irpSp->Parameters.UsageNotification.Type == DeviceUsageTypeHibernation && !HibernationEnabled) + ) + ) + { + IoReleaseRemoveLock (&Extension->Queue.RemoveLock, Irp); + + if (irpSp->Parameters.UsageNotification.Type == DeviceUsageTypeHibernation) + ++HibernationPreventionCount; + + Dump ("Preventing dump type=%d\n", (int) irpSp->Parameters.UsageNotification.Type); + return TCCompleteIrp (Irp, STATUS_UNSUCCESSFUL, 0); + } + + return PassFilteredIrp (Extension->LowerDeviceObject, Irp, OnDeviceUsageNotificationCompleted, Extension); + + + case IRP_MN_REMOVE_DEVICE: + Dump ("IRP_MN_REMOVE_DEVICE pdo=%p\n", Extension->Pdo); + + IoReleaseRemoveLockAndWait (&Extension->Queue.RemoveLock, Irp); + status = PassIrp (Extension->LowerDeviceObject, Irp); + + IoDetachDevice (Extension->LowerDeviceObject); + + if (Extension->DriveMounted) + DismountDrive (Extension, TRUE); + + if (Extension->BootDrive) + { + BootDriveFound = FALSE; + BootDriveFilterExtension = NULL; + } + + IoDeleteDevice (DeviceObject); + return status; + + + default: + status = PassIrp (Extension->LowerDeviceObject, Irp); + IoReleaseRemoveLock (&Extension->Queue.RemoveLock, Irp); + } + return status; +} + + +static NTSTATUS DispatchPower (PDEVICE_OBJECT DeviceObject, PIRP Irp, DriveFilterExtension *Extension, PIO_STACK_LOCATION irpSp) +{ + NTSTATUS status; + Dump ("IRP_MJ_POWER minor=%d type=%d shutdown=%d\n", (int) irpSp->MinorFunction, (int) irpSp->Parameters.Power.Type, (int) irpSp->Parameters.Power.ShutdownType); + + if (SetupInProgress + && irpSp->MinorFunction == IRP_MN_SET_POWER + && irpSp->Parameters.Power.ShutdownType == PowerActionHibernate) + { + while (SendDeviceIoControlRequest (RootDeviceObject, TC_IOCTL_ABORT_BOOT_ENCRYPTION_SETUP, NULL, 0, NULL, 0) == STATUS_INSUFFICIENT_RESOURCES); + } + +#if 0 // Dismount of the system drive is disabled until there is a way to do it without causing system errors (see the documentation for more info) + if (DriverShuttingDown + && Extension->BootDrive + && Extension->DriveMounted + && irpSp->MinorFunction == IRP_MN_SET_POWER + && irpSp->Parameters.Power.Type == DevicePowerState) + { + DismountDrive (Extension, TRUE); + } +#endif // 0 + + PoStartNextPowerIrp (Irp); + + status = IoAcquireRemoveLock (&Extension->Queue.RemoveLock, Irp); + if (!NT_SUCCESS (status)) + return TCCompleteIrp (Irp, status, 0); + + IoSkipCurrentIrpStackLocation (Irp); + status = PoCallDriver (Extension->LowerDeviceObject, Irp); + + IoReleaseRemoveLock (&Extension->Queue.RemoveLock, Irp); + return status; +} + + +NTSTATUS DriveFilterDispatchIrp (PDEVICE_OBJECT DeviceObject, PIRP Irp) +{ + DriveFilterExtension *Extension = (DriveFilterExtension *) DeviceObject->DeviceExtension; + PIO_STACK_LOCATION irpSp = IoGetCurrentIrpStackLocation (Irp); + NTSTATUS status; + + ASSERT (!Extension->bRootDevice && Extension->IsDriveFilterDevice); + + switch (irpSp->MajorFunction) + { + case IRP_MJ_READ: + case IRP_MJ_WRITE: + if (Extension->BootDrive) + { + status = EncryptedIoQueueAddIrp (&Extension->Queue, Irp); + + if (status != STATUS_PENDING) + TCCompleteDiskIrp (Irp, status, 0); + + return status; + } + break; + + case IRP_MJ_PNP: + return DispatchPnp (DeviceObject, Irp, Extension, irpSp); + + case IRP_MJ_POWER: + return DispatchPower (DeviceObject, Irp, Extension, irpSp); + } + + status = IoAcquireRemoveLock (&Extension->Queue.RemoveLock, Irp); + if (!NT_SUCCESS (status)) + return TCCompleteIrp (Irp, status, 0); + + status = PassIrp (Extension->LowerDeviceObject, Irp); + + IoReleaseRemoveLock (&Extension->Queue.RemoveLock, Irp); + return status; +} + + +void ReopenBootVolumeHeader (PIRP irp, PIO_STACK_LOCATION irpSp) +{ + LARGE_INTEGER offset; + char *header; + ReopenBootVolumeHeaderRequest *request = (ReopenBootVolumeHeaderRequest *) irp->AssociatedIrp.SystemBuffer; + + irp->IoStatus.Information = 0; + + if (!IoIsSystemThread (PsGetCurrentThread()) && !UserCanAccessDriveDevice()) + { + irp->IoStatus.Status = STATUS_ACCESS_DENIED; + return; + } + + if (!ValidateIOBufferSize (irp, sizeof (ReopenBootVolumeHeaderRequest), ValidateInput)) + return; + + if (!BootDriveFound || !BootDriveFilterExtension || !BootDriveFilterExtension->DriveMounted || !BootDriveFilterExtension->HeaderCryptoInfo + || request->VolumePassword.Length > MAX_PASSWORD + || request->pkcs5_prf < 0 + || request->pkcs5_prf > LAST_PRF_ID + || request->pim < 0 + || request->pim > 65535 + ) + { + irp->IoStatus.Status = STATUS_INVALID_PARAMETER; + goto wipe; + } + + header = TCalloc (TC_BOOT_ENCRYPTION_VOLUME_HEADER_SIZE); + if (!header) + { + irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES; + goto wipe; + } + + if (BootDriveFilterExtension->HiddenSystem) + offset.QuadPart = BootArgs.HiddenSystemPartitionStart + TC_HIDDEN_VOLUME_HEADER_OFFSET; + else + offset.QuadPart = TC_BOOT_VOLUME_HEADER_SECTOR_OFFSET; + + irp->IoStatus.Status = TCReadDevice (BootDriveFilterExtension->LowerDeviceObject, header, offset, TC_BOOT_ENCRYPTION_VOLUME_HEADER_SIZE); + if (!NT_SUCCESS (irp->IoStatus.Status)) + { + Dump ("TCReadDevice error %x\n", irp->IoStatus.Status); + goto ret; + } + + if (ReadVolumeHeader (!BootDriveFilterExtension->HiddenSystem, header, &request->VolumePassword, request->pkcs5_prf, request->pim, FALSE, NULL, BootDriveFilterExtension->HeaderCryptoInfo) == 0) + { + Dump ("Header reopened\n"); + ComputeBootLoaderFingerprint (BootDriveFilterExtension->LowerDeviceObject, header); + + BootDriveFilterExtension->Queue.CryptoInfo->header_creation_time = BootDriveFilterExtension->HeaderCryptoInfo->header_creation_time; + BootDriveFilterExtension->Queue.CryptoInfo->pkcs5 = BootDriveFilterExtension->HeaderCryptoInfo->pkcs5; + BootDriveFilterExtension->Queue.CryptoInfo->noIterations = BootDriveFilterExtension->HeaderCryptoInfo->noIterations; + BootDriveFilterExtension->Queue.CryptoInfo->volumePim = BootDriveFilterExtension->HeaderCryptoInfo->volumePim; + + irp->IoStatus.Status = STATUS_SUCCESS; + } + else + { + crypto_close (BootDriveFilterExtension->HeaderCryptoInfo); + BootDriveFilterExtension->HeaderCryptoInfo = NULL; + + Dump ("Header not reopened\n"); + irp->IoStatus.Status = STATUS_INVALID_PARAMETER; + } + +ret: + TCfree (header); +wipe: + burn (request, sizeof (*request)); +} + + +// Legacy Windows XP/2003 hibernation dump filter + +typedef NTSTATUS (*HiberDriverWriteFunctionA) (ULONG arg0, PLARGE_INTEGER writeOffset, PMDL dataMdl, PVOID arg3); +typedef NTSTATUS (*HiberDriverWriteFunctionB) (PLARGE_INTEGER writeOffset, PMDL dataMdl); + +typedef struct +{ +#ifdef _WIN64 + byte FieldPad1[64]; + HiberDriverWriteFunctionB WriteFunctionB; + byte FieldPad2[56]; +#else + byte FieldPad1[48]; + HiberDriverWriteFunctionB WriteFunctionB; + byte FieldPad2[32]; +#endif + HiberDriverWriteFunctionA WriteFunctionA; + byte FieldPad3[24]; + LARGE_INTEGER PartitionStartOffset; +} HiberDriverContext; + +typedef NTSTATUS (*HiberDriverEntry) (PVOID arg0, HiberDriverContext *hiberDriverContext); + +typedef struct +{ + LIST_ENTRY ModuleList; +#ifdef _WIN64 + byte FieldPad1[32]; +#else + byte FieldPad1[16]; +#endif + PVOID ModuleBaseAddress; + HiberDriverEntry ModuleEntryAddress; +#ifdef _WIN64 + byte FieldPad2[24]; +#else + byte FieldPad2[12]; +#endif + UNICODE_STRING ModuleName; +} ModuleTableItem; + + +#define TC_MAX_HIBER_FILTER_COUNT 3 +static int LastHiberFilterNumber = 0; + +static HiberDriverEntry OriginalHiberDriverEntries[TC_MAX_HIBER_FILTER_COUNT]; +static HiberDriverWriteFunctionA OriginalHiberDriverWriteFunctionsA[TC_MAX_HIBER_FILTER_COUNT]; +static HiberDriverWriteFunctionB OriginalHiberDriverWriteFunctionsB[TC_MAX_HIBER_FILTER_COUNT]; + +static LARGE_INTEGER HiberPartitionOffset; + + +static NTSTATUS HiberDriverWriteFunctionFilter (int filterNumber, PLARGE_INTEGER writeOffset, PMDL dataMdl, BOOL writeB, ULONG arg0WriteA, PVOID arg3WriteA) +{ + MDL *encryptedDataMdl = dataMdl; + + if (writeOffset && dataMdl && BootDriveFilterExtension && BootDriveFilterExtension->DriveMounted) + { + ULONG dataLength = MmGetMdlByteCount (dataMdl); + + if (dataMdl->MappedSystemVa && dataLength > 0) + { + uint64 offset = HiberPartitionOffset.QuadPart + writeOffset->QuadPart; + uint64 intersectStart; + uint32 intersectLength; + + if (dataLength > TC_HIBERNATION_WRITE_BUFFER_SIZE) + TC_BUG_CHECK (STATUS_BUFFER_OVERFLOW); + + if ((dataLength & (ENCRYPTION_DATA_UNIT_SIZE - 1)) != 0) + TC_BUG_CHECK (STATUS_INVALID_PARAMETER); + + if ((offset & (ENCRYPTION_DATA_UNIT_SIZE - 1)) != 0) + TC_BUG_CHECK (STATUS_INVALID_PARAMETER); + + GetIntersection (offset, + dataLength, + BootDriveFilterExtension->Queue.EncryptedAreaStart, + BootDriveFilterExtension->Queue.EncryptedAreaEnd, + &intersectStart, + &intersectLength); + + if (intersectLength > 0) + { + UINT64_STRUCT dataUnit; + dataUnit.Value = intersectStart / ENCRYPTION_DATA_UNIT_SIZE; + + memcpy (HibernationWriteBuffer, dataMdl->MappedSystemVa, dataLength); + + if (BootDriveFilterExtension->Queue.RemapEncryptedArea) + dataUnit.Value += BootDriveFilterExtension->Queue.RemappedAreaDataUnitOffset; + + EncryptDataUnitsCurrentThread (HibernationWriteBuffer + (intersectStart - offset), + &dataUnit, + intersectLength / ENCRYPTION_DATA_UNIT_SIZE, + BootDriveFilterExtension->Queue.CryptoInfo); + + encryptedDataMdl = HibernationWriteBufferMdl; + MmInitializeMdl (encryptedDataMdl, HibernationWriteBuffer, dataLength); + encryptedDataMdl->MdlFlags = dataMdl->MdlFlags; + } + } + } + + if (writeB) + return (*OriginalHiberDriverWriteFunctionsB[filterNumber]) (writeOffset, encryptedDataMdl); + + return (*OriginalHiberDriverWriteFunctionsA[filterNumber]) (arg0WriteA, writeOffset, encryptedDataMdl, arg3WriteA); +} + + +static NTSTATUS HiberDriverWriteFunctionAFilter0 (ULONG arg0, PLARGE_INTEGER writeOffset, PMDL dataMdl, PVOID arg3) +{ + return HiberDriverWriteFunctionFilter (0, writeOffset, dataMdl, FALSE, arg0, arg3); +} + +static NTSTATUS HiberDriverWriteFunctionAFilter1 (ULONG arg0, PLARGE_INTEGER writeOffset, PMDL dataMdl, PVOID arg3) +{ + return HiberDriverWriteFunctionFilter (1, writeOffset, dataMdl, FALSE, arg0, arg3); +} + +static NTSTATUS HiberDriverWriteFunctionAFilter2 (ULONG arg0, PLARGE_INTEGER writeOffset, PMDL dataMdl, PVOID arg3) +{ + return HiberDriverWriteFunctionFilter (2, writeOffset, dataMdl, FALSE, arg0, arg3); +} + + +static NTSTATUS HiberDriverWriteFunctionBFilter0 (PLARGE_INTEGER writeOffset, PMDL dataMdl) +{ + return HiberDriverWriteFunctionFilter (0, writeOffset, dataMdl, TRUE, 0, NULL); +} + +static NTSTATUS HiberDriverWriteFunctionBFilter1 (PLARGE_INTEGER writeOffset, PMDL dataMdl) +{ + return HiberDriverWriteFunctionFilter (1, writeOffset, dataMdl, TRUE, 0, NULL); +} + +static NTSTATUS HiberDriverWriteFunctionBFilter2 (PLARGE_INTEGER writeOffset, PMDL dataMdl) +{ + return HiberDriverWriteFunctionFilter (2, writeOffset, dataMdl, TRUE, 0, NULL); +} + + +static NTSTATUS HiberDriverEntryFilter (int filterNumber, PVOID arg0, HiberDriverContext *hiberDriverContext) +{ + BOOL filterInstalled = FALSE; + NTSTATUS status; + + if (!OriginalHiberDriverEntries[filterNumber]) + return STATUS_UNSUCCESSFUL; + + status = (*OriginalHiberDriverEntries[filterNumber]) (arg0, hiberDriverContext); + + if (!NT_SUCCESS (status) || !hiberDriverContext) + return status; + + if (SetupInProgress) + TC_BUG_CHECK (STATUS_INVALID_PARAMETER); + + if (hiberDriverContext->WriteFunctionA) + { + Dump ("Filtering WriteFunctionA %d\n", filterNumber); + OriginalHiberDriverWriteFunctionsA[filterNumber] = hiberDriverContext->WriteFunctionA; + + switch (filterNumber) + { + case 0: hiberDriverContext->WriteFunctionA = HiberDriverWriteFunctionAFilter0; break; + case 1: hiberDriverContext->WriteFunctionA = HiberDriverWriteFunctionAFilter1; break; + case 2: hiberDriverContext->WriteFunctionA = HiberDriverWriteFunctionAFilter2; break; + default: TC_THROW_FATAL_EXCEPTION; + } + + filterInstalled = TRUE; + } + + if (hiberDriverContext->WriteFunctionB) + { + Dump ("Filtering WriteFunctionB %d\n", filterNumber); + OriginalHiberDriverWriteFunctionsB[filterNumber] = hiberDriverContext->WriteFunctionB; + + switch (filterNumber) + { + case 0: hiberDriverContext->WriteFunctionB = HiberDriverWriteFunctionBFilter0; break; + case 1: hiberDriverContext->WriteFunctionB = HiberDriverWriteFunctionBFilter1; break; + case 2: hiberDriverContext->WriteFunctionB = HiberDriverWriteFunctionBFilter2; break; + default: TC_THROW_FATAL_EXCEPTION; + } + + filterInstalled = TRUE; + } + + if (filterInstalled && hiberDriverContext->PartitionStartOffset.QuadPart != 0) + { + HiberPartitionOffset = hiberDriverContext->PartitionStartOffset; + + if (BootDriveFilterExtension->Queue.RemapEncryptedArea) + hiberDriverContext->PartitionStartOffset.QuadPart += BootDriveFilterExtension->Queue.RemappedAreaOffset; + } + + return STATUS_SUCCESS; +} + + +static NTSTATUS HiberDriverEntryFilter0 (PVOID arg0, HiberDriverContext *hiberDriverContext) +{ + return HiberDriverEntryFilter (0, arg0, hiberDriverContext); +} + + +static NTSTATUS HiberDriverEntryFilter1 (PVOID arg0, HiberDriverContext *hiberDriverContext) +{ + return HiberDriverEntryFilter (1, arg0, hiberDriverContext); +} + + +static NTSTATUS HiberDriverEntryFilter2 (PVOID arg0, HiberDriverContext *hiberDriverContext) +{ + return HiberDriverEntryFilter (2, arg0, hiberDriverContext); +} + + +static VOID LoadImageNotifyRoutine (PUNICODE_STRING fullImageName, HANDLE processId, PIMAGE_INFO imageInfo) +{ + ModuleTableItem *moduleItem; + LIST_ENTRY *listEntry; + KIRQL origIrql; + + if (!imageInfo || !imageInfo->SystemModeImage || !imageInfo->ImageBase || !TCDriverObject->DriverSection) + return; + + moduleItem = *(ModuleTableItem **) TCDriverObject->DriverSection; + if (!moduleItem || !moduleItem->ModuleList.Flink) + return; + + // Search loaded system modules for hibernation driver + origIrql = KeRaiseIrqlToDpcLevel(); + + for (listEntry = moduleItem->ModuleList.Flink->Blink; + listEntry && listEntry != TCDriverObject->DriverSection; + listEntry = listEntry->Flink) + { + moduleItem = CONTAINING_RECORD (listEntry, ModuleTableItem, ModuleList); + + if (moduleItem && imageInfo->ImageBase == moduleItem->ModuleBaseAddress) + { + if (moduleItem->ModuleName.Buffer && moduleItem->ModuleName.Length >= 5 * sizeof (wchar_t)) + { + if (memcmp (moduleItem->ModuleName.Buffer, L"hiber", 5 * sizeof (wchar_t)) == 0 + || memcmp (moduleItem->ModuleName.Buffer, L"Hiber", 5 * sizeof (wchar_t)) == 0 + || memcmp (moduleItem->ModuleName.Buffer, L"HIBER", 5 * sizeof (wchar_t)) == 0) + { + HiberDriverEntry filterEntry; + + switch (LastHiberFilterNumber) + { + case 0: filterEntry = HiberDriverEntryFilter0; break; + case 1: filterEntry = HiberDriverEntryFilter1; break; + case 2: filterEntry = HiberDriverEntryFilter2; break; + default: TC_THROW_FATAL_EXCEPTION; + } + + if (moduleItem->ModuleEntryAddress != filterEntry) + { + // Install filter + OriginalHiberDriverEntries[LastHiberFilterNumber] = moduleItem->ModuleEntryAddress; + moduleItem->ModuleEntryAddress = filterEntry; + + if (++LastHiberFilterNumber > TC_MAX_HIBER_FILTER_COUNT - 1) + LastHiberFilterNumber = 0; + } + } + } + break; + } + } + + KeLowerIrql (origIrql); +} + + +void StartLegacyHibernationDriverFilter () +{ + PHYSICAL_ADDRESS highestAcceptableWriteBufferAddr; + NTSTATUS status; + + ASSERT (KeGetCurrentIrql() == PASSIVE_LEVEL); + ASSERT (!IsOSAtLeast (WIN_VISTA)); + + if (!TCDriverObject->DriverSection || !*(ModuleTableItem **) TCDriverObject->DriverSection) + goto err; + + // All buffers required for hibernation must be allocated here +#ifdef _WIN64 + highestAcceptableWriteBufferAddr.QuadPart = 0x7FFffffFFFFULL; +#else + highestAcceptableWriteBufferAddr.QuadPart = 0xffffFFFFULL; +#endif + + HibernationWriteBuffer = MmAllocateContiguousMemory (TC_HIBERNATION_WRITE_BUFFER_SIZE, highestAcceptableWriteBufferAddr); + if (!HibernationWriteBuffer) + goto err; + + HibernationWriteBufferMdl = IoAllocateMdl (HibernationWriteBuffer, TC_HIBERNATION_WRITE_BUFFER_SIZE, FALSE, FALSE, NULL); + if (!HibernationWriteBufferMdl) + goto err; + + MmBuildMdlForNonPagedPool (HibernationWriteBufferMdl); + + status = PsSetLoadImageNotifyRoutine (LoadImageNotifyRoutine); + if (!NT_SUCCESS (status)) + goto err; + + LegacyHibernationDriverFilterActive = TRUE; + CrashDumpEnabled = FALSE; + HibernationEnabled = TRUE; + return; + +err: + LegacyHibernationDriverFilterActive = FALSE; + CrashDumpEnabled = FALSE; + HibernationEnabled = FALSE; + + if (HibernationWriteBufferMdl) + { + IoFreeMdl (HibernationWriteBufferMdl); + HibernationWriteBufferMdl = NULL; + } + + if (HibernationWriteBuffer) + { + MmFreeContiguousMemory (HibernationWriteBuffer); + HibernationWriteBuffer = NULL; + } +} + + +static VOID SetupThreadProc (PVOID threadArg) +{ + DriveFilterExtension *Extension = BootDriveFilterExtension; + + LARGE_INTEGER offset; + UINT64_STRUCT dataUnit; + ULONG setupBlockSize = TC_ENCRYPTION_SETUP_IO_BLOCK_SIZE; + BOOL headerUpdateRequired = FALSE; + int64 bytesWrittenSinceHeaderUpdate = 0; + + byte *buffer = NULL; + byte *wipeBuffer = NULL; + byte wipeRandChars[TC_WIPE_RAND_CHAR_COUNT]; + byte wipeRandCharsUpdate[TC_WIPE_RAND_CHAR_COUNT]; + + KIRQL irql; + NTSTATUS status; + + // generate real random values for wipeRandChars and + // wipeRandCharsUpdate instead of relying on uninitialized stack memory + LARGE_INTEGER iSeed; + KeQuerySystemTime( &iSeed ); + if (KeGetCurrentIrql() < DISPATCH_LEVEL) + { + ULONG ulRandom; + ulRandom = RtlRandomEx( &iSeed.LowPart ); + memcpy (wipeRandChars, &ulRandom, TC_WIPE_RAND_CHAR_COUNT); + ulRandom = RtlRandomEx( &ulRandom ); + memcpy (wipeRandCharsUpdate, &ulRandom, TC_WIPE_RAND_CHAR_COUNT); + burn (&ulRandom, sizeof(ulRandom)); + } + else + { + byte digest[SHA512_DIGESTSIZE]; + sha512_ctx tctx; + sha512_begin (&tctx); + sha512_hash ((unsigned char *) &(iSeed.QuadPart), sizeof(iSeed.QuadPart), &tctx); + sha512_end (digest, &tctx); + + memcpy (wipeRandChars, digest, TC_WIPE_RAND_CHAR_COUNT); + memcpy (wipeRandCharsUpdate, &digest[SHA512_DIGESTSIZE - TC_WIPE_RAND_CHAR_COUNT], TC_WIPE_RAND_CHAR_COUNT); + + burn (digest, SHA512_DIGESTSIZE); + burn (&tctx, sizeof (tctx)); + } + + burn (&iSeed, sizeof(iSeed)); + + SetupResult = STATUS_UNSUCCESSFUL; + + // Make sure volume header can be updated + if (Extension->HeaderCryptoInfo == NULL) + { + SetupResult = STATUS_INVALID_PARAMETER; + goto ret; + } + + buffer = TCalloc (TC_ENCRYPTION_SETUP_IO_BLOCK_SIZE); + if (!buffer) + { + SetupResult = STATUS_INSUFFICIENT_RESOURCES; + goto ret; + } + + if (SetupRequest.SetupMode == SetupEncryption && SetupRequest.WipeAlgorithm != TC_WIPE_NONE) + { + wipeBuffer = TCalloc (TC_ENCRYPTION_SETUP_IO_BLOCK_SIZE); + if (!wipeBuffer) + { + SetupResult = STATUS_INSUFFICIENT_RESOURCES; + goto ret; + } + } + + while (!NT_SUCCESS (EncryptedIoQueueHoldWhenIdle (&Extension->Queue, 1000))) + { + if (EncryptionSetupThreadAbortRequested) + goto abort; + + TransformWaitingForIdle = TRUE; + } + TransformWaitingForIdle = FALSE; + + switch (SetupRequest.SetupMode) + { + case SetupEncryption: + Dump ("Encrypting...\n"); + if (Extension->Queue.EncryptedAreaStart == -1 || Extension->Queue.EncryptedAreaEnd == -1) + { + // Start encryption + Extension->Queue.EncryptedAreaStart = Extension->ConfiguredEncryptedAreaStart; + Extension->Queue.EncryptedAreaEnd = -1; + offset.QuadPart = Extension->ConfiguredEncryptedAreaStart; + } + else + { + // Resume aborted encryption + if (Extension->Queue.EncryptedAreaEnd == Extension->ConfiguredEncryptedAreaEnd) + goto err; + + offset.QuadPart = Extension->Queue.EncryptedAreaEnd + 1; + } + + break; + + case SetupDecryption: + Dump ("Decrypting...\n"); + if (Extension->Queue.EncryptedAreaStart == -1 || Extension->Queue.EncryptedAreaEnd == -1) + { + SetupResult = STATUS_SUCCESS; + goto abort; + } + + offset.QuadPart = Extension->Queue.EncryptedAreaEnd + 1; + break; + + default: + goto err; + } + + EncryptedIoQueueResumeFromHold (&Extension->Queue); + + Dump ("EncryptedAreaStart=%I64d\n", Extension->Queue.EncryptedAreaStart); + Dump ("EncryptedAreaEnd=%I64d\n", Extension->Queue.EncryptedAreaEnd); + Dump ("ConfiguredEncryptedAreaStart=%I64d\n", Extension->ConfiguredEncryptedAreaStart); + Dump ("ConfiguredEncryptedAreaEnd=%I64d\n", Extension->ConfiguredEncryptedAreaEnd); + Dump ("offset=%I64d\n", offset.QuadPart); + Dump ("EncryptedAreaStart=%I64d (%I64d) EncryptedAreaEnd=%I64d\n", Extension->Queue.EncryptedAreaStart / 1024 / 1024, Extension->Queue.EncryptedAreaStart, Extension->Queue.EncryptedAreaEnd / 1024 / 1024); + + while (!EncryptionSetupThreadAbortRequested) + { + if (SetupRequest.SetupMode == SetupEncryption) + { + if (offset.QuadPart + setupBlockSize > Extension->ConfiguredEncryptedAreaEnd + 1) + setupBlockSize = (ULONG) (Extension->ConfiguredEncryptedAreaEnd + 1 - offset.QuadPart); + + if (offset.QuadPart > Extension->ConfiguredEncryptedAreaEnd) + break; + } + else + { + if (offset.QuadPart - setupBlockSize < Extension->Queue.EncryptedAreaStart) + setupBlockSize = (ULONG) (offset.QuadPart - Extension->Queue.EncryptedAreaStart); + + offset.QuadPart -= setupBlockSize; + + if (setupBlockSize == 0 || offset.QuadPart < Extension->Queue.EncryptedAreaStart) + break; + } + + while (!NT_SUCCESS (EncryptedIoQueueHoldWhenIdle (&Extension->Queue, 500))) + { + if (EncryptionSetupThreadAbortRequested) + goto abort; + + TransformWaitingForIdle = TRUE; + } + TransformWaitingForIdle = FALSE; + + status = TCReadDevice (BootDriveFilterExtension->LowerDeviceObject, buffer, offset, setupBlockSize); + if (!NT_SUCCESS (status)) + { + Dump ("TCReadDevice error %x offset=%I64d\n", status, offset.QuadPart); + + if (SetupRequest.ZeroUnreadableSectors && SetupRequest.SetupMode == SetupEncryption) + { + // Zero unreadable sectors + uint64 zeroedSectorCount; + + status = ZeroUnreadableSectors (BootDriveFilterExtension->LowerDeviceObject, offset, setupBlockSize, &zeroedSectorCount); + if (!NT_SUCCESS (status)) + { + SetupResult = status; + goto err; + } + + // Retry read + status = TCReadDevice (BootDriveFilterExtension->LowerDeviceObject, buffer, offset, setupBlockSize); + if (!NT_SUCCESS (status)) + { + SetupResult = status; + goto err; + } + } + else if (SetupRequest.DiscardUnreadableEncryptedSectors && SetupRequest.SetupMode == SetupDecryption) + { + // Discard unreadable encrypted sectors + uint64 badSectorCount; + + status = ReadDeviceSkipUnreadableSectors (BootDriveFilterExtension->LowerDeviceObject, buffer, offset, setupBlockSize, &badSectorCount); + if (!NT_SUCCESS (status)) + { + SetupResult = status; + goto err; + } + } + else + { + SetupResult = status; + goto err; + } + } + + dataUnit.Value = offset.QuadPart / ENCRYPTION_DATA_UNIT_SIZE; + + if (SetupRequest.SetupMode == SetupEncryption) + { + EncryptDataUnits (buffer, &dataUnit, setupBlockSize / ENCRYPTION_DATA_UNIT_SIZE, Extension->Queue.CryptoInfo); + + if (SetupRequest.WipeAlgorithm != TC_WIPE_NONE) + { + byte wipePass; + int wipePassCount = GetWipePassCount (SetupRequest.WipeAlgorithm); + if (wipePassCount <= 0) + { + SetupResult = STATUS_INVALID_PARAMETER; + goto err; + } + + for (wipePass = 1; wipePass <= wipePassCount; ++wipePass) + { + if (!WipeBuffer (SetupRequest.WipeAlgorithm, wipeRandChars, wipePass, wipeBuffer, setupBlockSize)) + { + ULONG i; + for (i = 0; i < setupBlockSize; ++i) + { + wipeBuffer[i] = buffer[i] + wipePass; + } + + EncryptDataUnits (wipeBuffer, &dataUnit, setupBlockSize / ENCRYPTION_DATA_UNIT_SIZE, Extension->Queue.CryptoInfo); + memcpy (wipeRandCharsUpdate, wipeBuffer, sizeof (wipeRandCharsUpdate)); + } + + status = TCWriteDevice (BootDriveFilterExtension->LowerDeviceObject, wipeBuffer, offset, setupBlockSize); + if (!NT_SUCCESS (status)) + { + // Undo failed write operation + DecryptDataUnits (buffer, &dataUnit, setupBlockSize / ENCRYPTION_DATA_UNIT_SIZE, Extension->Queue.CryptoInfo); + TCWriteDevice (BootDriveFilterExtension->LowerDeviceObject, buffer, offset, setupBlockSize); + + SetupResult = status; + goto err; + } + } + + memcpy (wipeRandChars, wipeRandCharsUpdate, sizeof (wipeRandCharsUpdate)); + } + } + else + { + DecryptDataUnits (buffer, &dataUnit, setupBlockSize / ENCRYPTION_DATA_UNIT_SIZE, Extension->Queue.CryptoInfo); + } + + status = TCWriteDevice (BootDriveFilterExtension->LowerDeviceObject, buffer, offset, setupBlockSize); + if (!NT_SUCCESS (status)) + { + Dump ("TCWriteDevice error %x\n", status); + + // Undo failed write operation + if (SetupRequest.SetupMode == SetupEncryption) + DecryptDataUnits (buffer, &dataUnit, setupBlockSize / ENCRYPTION_DATA_UNIT_SIZE, Extension->Queue.CryptoInfo); + else + EncryptDataUnits (buffer, &dataUnit, setupBlockSize / ENCRYPTION_DATA_UNIT_SIZE, Extension->Queue.CryptoInfo); + + TCWriteDevice (BootDriveFilterExtension->LowerDeviceObject, buffer, offset, setupBlockSize); + + SetupResult = status; + goto err; + } + + if (SetupRequest.SetupMode == SetupEncryption) + offset.QuadPart += setupBlockSize; + + Extension->Queue.EncryptedAreaEndUpdatePending = TRUE; + Extension->Queue.EncryptedAreaEnd = offset.QuadPart - 1; + Extension->Queue.EncryptedAreaEndUpdatePending = FALSE; + + headerUpdateRequired = TRUE; + + EncryptedIoQueueResumeFromHold (&Extension->Queue); + + KeAcquireSpinLock (&SetupStatusSpinLock, &irql); + SetupStatusEncryptedAreaEnd = Extension->Queue.EncryptedAreaEnd; + KeReleaseSpinLock (&SetupStatusSpinLock, irql); + + // Update volume header + bytesWrittenSinceHeaderUpdate += setupBlockSize; + if (bytesWrittenSinceHeaderUpdate >= TC_ENCRYPTION_SETUP_HEADER_UPDATE_THRESHOLD) + { + status = SaveDriveVolumeHeader (Extension); + ASSERT (NT_SUCCESS (status)); + if (NT_SUCCESS (status)) + { + headerUpdateRequired = FALSE; + bytesWrittenSinceHeaderUpdate = 0; + } + } + } + +abort: + SetupResult = STATUS_SUCCESS; +err: + + if (Extension->Queue.EncryptedAreaEnd == -1) + Extension->Queue.EncryptedAreaStart = -1; + + if (EncryptedIoQueueIsSuspended (&Extension->Queue)) + EncryptedIoQueueResumeFromHold (&Extension->Queue); + + if (SetupRequest.SetupMode == SetupDecryption && Extension->Queue.EncryptedAreaStart >= Extension->Queue.EncryptedAreaEnd) + { + while (!NT_SUCCESS (EncryptedIoQueueHoldWhenIdle (&Extension->Queue, 0))); + + Extension->ConfiguredEncryptedAreaStart = Extension->ConfiguredEncryptedAreaEnd = -1; + Extension->Queue.EncryptedAreaStart = Extension->Queue.EncryptedAreaEnd = -1; + + EncryptedIoQueueResumeFromHold (&Extension->Queue); + + headerUpdateRequired = TRUE; + } + + Dump ("Setup completed: EncryptedAreaStart=%I64d (%I64d) EncryptedAreaEnd=%I64d (%I64d)\n", Extension->Queue.EncryptedAreaStart / 1024 / 1024, Extension->Queue.EncryptedAreaStart, Extension->Queue.EncryptedAreaEnd / 1024 / 1024, Extension->Queue.EncryptedAreaEnd); + + if (headerUpdateRequired) + { + status = SaveDriveVolumeHeader (Extension); + + if (!NT_SUCCESS (status) && NT_SUCCESS (SetupResult)) + SetupResult = status; + } + + if (SetupRequest.SetupMode == SetupDecryption && Extension->ConfiguredEncryptedAreaEnd == -1 && Extension->DriveMounted) + { + while (!RootDeviceControlMutexAcquireNoWait() && !EncryptionSetupThreadAbortRequested) + { + TCSleep (10); + } + + // Disable hibernation (resume would fail due to a change in the system memory map) + HibernationEnabled = FALSE; + + DismountDrive (Extension, FALSE); + + if (!EncryptionSetupThreadAbortRequested) + RootDeviceControlMutexRelease(); + } + +ret: + if (buffer) + { + burn (buffer, TC_ENCRYPTION_SETUP_IO_BLOCK_SIZE); + TCfree (buffer); + } + if (wipeBuffer) + { + burn (wipeBuffer, TC_ENCRYPTION_SETUP_IO_BLOCK_SIZE); + TCfree (wipeBuffer); + } + + burn (wipeRandChars, TC_WIPE_RAND_CHAR_COUNT); + burn (wipeRandCharsUpdate, TC_WIPE_RAND_CHAR_COUNT); + + SetupInProgress = FALSE; + PsTerminateSystemThread (SetupResult); +} + + +NTSTATUS StartBootEncryptionSetup (PDEVICE_OBJECT DeviceObject, PIRP irp, PIO_STACK_LOCATION irpSp) +{ + NTSTATUS status; + + if (!UserCanAccessDriveDevice()) + return STATUS_ACCESS_DENIED; + + if (SetupInProgress || !BootDriveFound || !BootDriveFilterExtension + || !BootDriveFilterExtension->DriveMounted + || BootDriveFilterExtension->HiddenSystem + || irpSp->Parameters.DeviceIoControl.InputBufferLength < sizeof (BootEncryptionSetupRequest)) + return STATUS_INVALID_PARAMETER; + + if (EncryptionSetupThread) + AbortBootEncryptionSetup(); + + SetupRequest = *(BootEncryptionSetupRequest *) irp->AssociatedIrp.SystemBuffer; + + EncryptionSetupThreadAbortRequested = FALSE; + KeInitializeSpinLock (&SetupStatusSpinLock); + SetupStatusEncryptedAreaEnd = BootDriveFilterExtension ? BootDriveFilterExtension->Queue.EncryptedAreaEnd : -1; + + SetupInProgress = TRUE; + status = TCStartThread (SetupThreadProc, DeviceObject, &EncryptionSetupThread); + + if (!NT_SUCCESS (status)) + SetupInProgress = FALSE; + + return status; +} + + +void GetBootDriveVolumeProperties (PIRP irp, PIO_STACK_LOCATION irpSp) +{ + if (ValidateIOBufferSize (irp, sizeof (VOLUME_PROPERTIES_STRUCT), ValidateOutput)) + { + DriveFilterExtension *Extension = BootDriveFilterExtension; + VOLUME_PROPERTIES_STRUCT *prop = (VOLUME_PROPERTIES_STRUCT *) irp->AssociatedIrp.SystemBuffer; + memset (prop, 0, sizeof (*prop)); + + if (!BootDriveFound || !Extension || !Extension->DriveMounted) + { + irp->IoStatus.Status = STATUS_INVALID_PARAMETER; + irp->IoStatus.Information = 0; + } + else + { + prop->hiddenVolume = Extension->Queue.CryptoInfo->hiddenVolume; + prop->diskLength = Extension->ConfiguredEncryptedAreaEnd + 1 - Extension->ConfiguredEncryptedAreaStart; + prop->ea = Extension->Queue.CryptoInfo->ea; + prop->mode = Extension->Queue.CryptoInfo->mode; + prop->pkcs5 = Extension->Queue.CryptoInfo->pkcs5; + prop->pkcs5Iterations = Extension->Queue.CryptoInfo->noIterations; + prop->volumePim = Extension->Queue.CryptoInfo->volumePim; +#if 0 + prop->volumeCreationTime = Extension->Queue.CryptoInfo->volume_creation_time; + prop->headerCreationTime = Extension->Queue.CryptoInfo->header_creation_time; +#endif + prop->volFormatVersion = Extension->Queue.CryptoInfo->LegacyVolume ? TC_VOLUME_FORMAT_VERSION_PRE_6_0 : TC_VOLUME_FORMAT_VERSION; + + prop->totalBytesRead = Extension->Queue.TotalBytesRead; + prop->totalBytesWritten = Extension->Queue.TotalBytesWritten; + + irp->IoStatus.Information = sizeof (VOLUME_PROPERTIES_STRUCT); + irp->IoStatus.Status = STATUS_SUCCESS; + } + } +} + + +void GetBootEncryptionStatus (PIRP irp, PIO_STACK_LOCATION irpSp) +{ + /* IMPORTANT: Do NOT add any potentially time-consuming operations to this function. */ + + if (ValidateIOBufferSize (irp, sizeof (BootEncryptionStatus), ValidateOutput)) + { + DriveFilterExtension *Extension = BootDriveFilterExtension; + BootEncryptionStatus *bootEncStatus = (BootEncryptionStatus *) irp->AssociatedIrp.SystemBuffer; + memset (bootEncStatus, 0, sizeof (*bootEncStatus)); + + if (BootArgsValid) + bootEncStatus->BootLoaderVersion = BootArgs.BootLoaderVersion; + + bootEncStatus->DeviceFilterActive = DeviceFilterActive; + bootEncStatus->SetupInProgress = SetupInProgress; + bootEncStatus->SetupMode = SetupRequest.SetupMode; + bootEncStatus->TransformWaitingForIdle = TransformWaitingForIdle; + + if (!BootDriveFound || !Extension || !Extension->DriveMounted) + { + bootEncStatus->DriveEncrypted = FALSE; + bootEncStatus->DriveMounted = FALSE; + bootEncStatus->VolumeHeaderPresent = FALSE; + } + else + { + bootEncStatus->DriveMounted = Extension->DriveMounted; + bootEncStatus->VolumeHeaderPresent = Extension->VolumeHeaderPresent; + bootEncStatus->DriveEncrypted = Extension->Queue.EncryptedAreaStart != -1; + bootEncStatus->BootDriveLength = BootDriveLength; + + bootEncStatus->ConfiguredEncryptedAreaStart = Extension->ConfiguredEncryptedAreaStart; + bootEncStatus->ConfiguredEncryptedAreaEnd = Extension->ConfiguredEncryptedAreaEnd; + bootEncStatus->EncryptedAreaStart = Extension->Queue.EncryptedAreaStart; + + if (SetupInProgress) + { + KIRQL irql; + KeAcquireSpinLock (&SetupStatusSpinLock, &irql); + bootEncStatus->EncryptedAreaEnd = SetupStatusEncryptedAreaEnd; + KeReleaseSpinLock (&SetupStatusSpinLock, irql); + } + else + bootEncStatus->EncryptedAreaEnd = Extension->Queue.EncryptedAreaEnd; + + bootEncStatus->VolumeHeaderSaltCrc32 = Extension->VolumeHeaderSaltCrc32; + bootEncStatus->HibernationPreventionCount = HibernationPreventionCount; + bootEncStatus->HiddenSysLeakProtectionCount = HiddenSysLeakProtectionCount; + + bootEncStatus->HiddenSystem = Extension->HiddenSystem; + + if (Extension->HiddenSystem) + bootEncStatus->HiddenSystemPartitionStart = BootArgs.HiddenSystemPartitionStart; + } + + irp->IoStatus.Information = sizeof (BootEncryptionStatus); + irp->IoStatus.Status = STATUS_SUCCESS; + } +} + + +void GetBootLoaderVersion (PIRP irp, PIO_STACK_LOCATION irpSp) +{ + if (ValidateIOBufferSize (irp, sizeof (uint16), ValidateOutput)) + { + if (BootArgsValid) + { + *(uint16 *) irp->AssociatedIrp.SystemBuffer = BootArgs.BootLoaderVersion; + irp->IoStatus.Information = sizeof (uint16); + irp->IoStatus.Status = STATUS_SUCCESS; + } + else + { + irp->IoStatus.Status = STATUS_INVALID_PARAMETER; + irp->IoStatus.Information = 0; + } + } +} + +void GetBootLoaderFingerprint (PIRP irp, PIO_STACK_LOCATION irpSp) +{ + if (ValidateIOBufferSize (irp, sizeof (BootLoaderFingerprintRequest), ValidateOutput)) + { + irp->IoStatus.Information = 0; + if (BootArgsValid && BootDriveFound && BootDriveFilterExtension && BootDriveFilterExtension->DriveMounted && BootDriveFilterExtension->HeaderCryptoInfo) + { + BootLoaderFingerprintRequest *bootLoaderFingerprint = (BootLoaderFingerprintRequest *) irp->AssociatedIrp.SystemBuffer; + + /* compute the fingerprint again and check if it is the same as the one retrieved during boot */ + char *header = TCalloc (TC_BOOT_ENCRYPTION_VOLUME_HEADER_SIZE); + if (!header) + { + irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES; + } + else + { + memcpy (bootLoaderFingerprint->Fingerprint, BootLoaderFingerprint, sizeof (BootLoaderFingerprint)); + ComputeBootLoaderFingerprint (BootDriveFilterExtension->LowerDeviceObject, header); + + burn (header, TC_BOOT_ENCRYPTION_VOLUME_HEADER_SIZE); + TCfree (header); + + if (0 == memcmp (bootLoaderFingerprint->Fingerprint, BootLoaderFingerprint, sizeof (BootLoaderFingerprint))) + { + irp->IoStatus.Information = sizeof (BootLoaderFingerprintRequest); + irp->IoStatus.Status = STATUS_SUCCESS; + } + else + { + /* fingerprint mismatch.*/ + irp->IoStatus.Status = STATUS_INVALID_IMAGE_HASH; + } + } + } + else + { + irp->IoStatus.Status = STATUS_INVALID_PARAMETER; + } + } +} + +void GetBootEncryptionAlgorithmName (PIRP irp, PIO_STACK_LOCATION irpSp) +{ + if (ValidateIOBufferSize (irp, sizeof (GetBootEncryptionAlgorithmNameRequest), ValidateOutput)) + { + if (BootDriveFilterExtension && BootDriveFilterExtension->DriveMounted) + { + wchar_t BootEncryptionAlgorithmNameW[256]; + wchar_t BootPrfAlgorithmNameW[256]; + GetBootEncryptionAlgorithmNameRequest *request = (GetBootEncryptionAlgorithmNameRequest *) irp->AssociatedIrp.SystemBuffer; + EAGetName (BootEncryptionAlgorithmNameW, BootDriveFilterExtension->Queue.CryptoInfo->ea, 0); + HashGetName2 (BootPrfAlgorithmNameW, BootDriveFilterExtension->Queue.CryptoInfo->pkcs5); + + RtlStringCbPrintfA (request->BootEncryptionAlgorithmName, sizeof (request->BootEncryptionAlgorithmName), "%S", BootEncryptionAlgorithmNameW); + RtlStringCbPrintfA (request->BootPrfAlgorithmName, sizeof (request->BootPrfAlgorithmName), "%S", BootPrfAlgorithmNameW); + + irp->IoStatus.Information = sizeof (GetBootEncryptionAlgorithmNameRequest); + irp->IoStatus.Status = STATUS_SUCCESS; + } + else + { + irp->IoStatus.Status = STATUS_INVALID_PARAMETER; + irp->IoStatus.Information = 0; + } + } +} + + +NTSTATUS GetSetupResult() +{ + return SetupResult; +} + + +BOOL IsBootDriveMounted () +{ + return BootDriveFilterExtension && BootDriveFilterExtension->DriveMounted; +} + + +BOOL IsBootEncryptionSetupInProgress () +{ + return SetupInProgress; +} + + +BOOL IsHiddenSystemRunning () +{ + return BootDriveFilterExtension && BootDriveFilterExtension->HiddenSystem; +} + + +DriveFilterExtension *GetBootDriveFilterExtension () +{ + return BootDriveFilterExtension; +} + + +CRYPTO_INFO *GetSystemDriveCryptoInfo () +{ + return BootDriveFilterExtension->Queue.CryptoInfo; +} + + +NTSTATUS AbortBootEncryptionSetup () +{ + if (!IoIsSystemThread (PsGetCurrentThread()) && !UserCanAccessDriveDevice()) + return STATUS_ACCESS_DENIED; + + if (EncryptionSetupThread) + { + EncryptionSetupThreadAbortRequested = TRUE; + + TCStopThread (EncryptionSetupThread, NULL); + EncryptionSetupThread = NULL; + } + + return STATUS_SUCCESS; +} + + +static VOID DecoySystemWipeThreadProc (PVOID threadArg) +{ + DriveFilterExtension *Extension = BootDriveFilterExtension; + + LARGE_INTEGER offset; + UINT64_STRUCT dataUnit; + ULONG wipeBlockSize = TC_ENCRYPTION_SETUP_IO_BLOCK_SIZE; + + CRYPTO_INFO *wipeCryptoInfo = NULL; + byte *wipeBuffer = NULL; + byte *wipeRandBuffer = NULL; + byte wipeRandChars[TC_WIPE_RAND_CHAR_COUNT]; + int wipePass, wipePassCount; + int ea = Extension->Queue.CryptoInfo->ea; + + KIRQL irql; + NTSTATUS status; + + DecoySystemWipeResult = STATUS_UNSUCCESSFUL; + + wipeBuffer = TCalloc (TC_ENCRYPTION_SETUP_IO_BLOCK_SIZE); + if (!wipeBuffer) + { + DecoySystemWipeResult = STATUS_INSUFFICIENT_RESOURCES; + goto ret; + } + + wipeRandBuffer = TCalloc (TC_ENCRYPTION_SETUP_IO_BLOCK_SIZE); + if (!wipeRandBuffer) + { + DecoySystemWipeResult = STATUS_INSUFFICIENT_RESOURCES; + goto ret; + } + + wipeCryptoInfo = crypto_open(); + if (!wipeCryptoInfo) + { + DecoySystemWipeResult = STATUS_INSUFFICIENT_RESOURCES; + goto ret; + } + + wipeCryptoInfo->ea = ea; + wipeCryptoInfo->mode = Extension->Queue.CryptoInfo->mode; + + if (EAInit (ea, WipeDecoyRequest.WipeKey, wipeCryptoInfo->ks) != ERR_SUCCESS) + { + DecoySystemWipeResult = STATUS_INVALID_PARAMETER; + goto ret; + } + + memcpy (wipeCryptoInfo->k2, WipeDecoyRequest.WipeKey + EAGetKeySize (ea), EAGetKeySize (ea)); + + if (!EAInitMode (wipeCryptoInfo)) + { + DecoySystemWipeResult = STATUS_INVALID_PARAMETER; + goto err; + } + + EncryptDataUnits (wipeRandBuffer, &dataUnit, wipeBlockSize / ENCRYPTION_DATA_UNIT_SIZE, wipeCryptoInfo); + memcpy (wipeRandChars, wipeRandBuffer, sizeof (wipeRandChars)); + + burn (WipeDecoyRequest.WipeKey, sizeof (WipeDecoyRequest.WipeKey)); + + offset.QuadPart = Extension->ConfiguredEncryptedAreaStart; + + Dump ("Wiping decoy system: start offset = %I64d\n", offset.QuadPart); + + while (!DecoySystemWipeThreadAbortRequested) + { + if (offset.QuadPart + wipeBlockSize > Extension->ConfiguredEncryptedAreaEnd + 1) + wipeBlockSize = (ULONG) (Extension->ConfiguredEncryptedAreaEnd + 1 - offset.QuadPart); + + if (offset.QuadPart > Extension->ConfiguredEncryptedAreaEnd) + break; + + wipePassCount = GetWipePassCount (WipeDecoyRequest.WipeAlgorithm); + if (wipePassCount <= 0) + { + DecoySystemWipeResult = STATUS_INVALID_PARAMETER; + goto err; + } + + for (wipePass = 1; wipePass <= wipePassCount; ++wipePass) + { + if (!WipeBuffer (WipeDecoyRequest.WipeAlgorithm, wipeRandChars, wipePass, wipeBuffer, wipeBlockSize)) + { + dataUnit.Value = offset.QuadPart / ENCRYPTION_DATA_UNIT_SIZE; + EncryptDataUnits (wipeRandBuffer, &dataUnit, wipeBlockSize / ENCRYPTION_DATA_UNIT_SIZE, wipeCryptoInfo); + memcpy (wipeBuffer, wipeRandBuffer, wipeBlockSize); + } + + while (!NT_SUCCESS (EncryptedIoQueueHoldWhenIdle (&Extension->Queue, 500))) + { + if (DecoySystemWipeThreadAbortRequested) + goto abort; + } + + status = TCWriteDevice (BootDriveFilterExtension->LowerDeviceObject, wipeBuffer, offset, wipeBlockSize); + + if (!NT_SUCCESS (status)) + { + DecoySystemWipeResult = status; + goto err; + } + + EncryptedIoQueueResumeFromHold (&Extension->Queue); + } + + offset.QuadPart += wipeBlockSize; + + KeAcquireSpinLock (&DecoySystemWipeStatusSpinLock, &irql); + DecoySystemWipedAreaEnd = offset.QuadPart - 1; + KeReleaseSpinLock (&DecoySystemWipeStatusSpinLock, irql); + } + +abort: + DecoySystemWipeResult = STATUS_SUCCESS; +err: + + if (EncryptedIoQueueIsSuspended (&Extension->Queue)) + EncryptedIoQueueResumeFromHold (&Extension->Queue); + + Dump ("Wipe end: DecoySystemWipedAreaEnd=%I64d (%I64d)\n", DecoySystemWipedAreaEnd, DecoySystemWipedAreaEnd / 1024 / 1024); + +ret: + if (wipeCryptoInfo) + crypto_close (wipeCryptoInfo); + + if (wipeRandBuffer) + TCfree (wipeRandBuffer); + + if (wipeBuffer) + TCfree (wipeBuffer); + + DecoySystemWipeInProgress = FALSE; + PsTerminateSystemThread (DecoySystemWipeResult); +} + + +NTSTATUS StartDecoySystemWipe (PDEVICE_OBJECT DeviceObject, PIRP irp, PIO_STACK_LOCATION irpSp) +{ + NTSTATUS status; + WipeDecoySystemRequest *request; + + if (!UserCanAccessDriveDevice()) + return STATUS_ACCESS_DENIED; + + if (!IsHiddenSystemRunning() + || irpSp->Parameters.DeviceIoControl.InputBufferLength < sizeof (WipeDecoySystemRequest)) + return STATUS_INVALID_PARAMETER; + + if (DecoySystemWipeInProgress) + return STATUS_SUCCESS; + + if (DecoySystemWipeThread) + AbortDecoySystemWipe(); + + request = (WipeDecoySystemRequest *) irp->AssociatedIrp.SystemBuffer; + WipeDecoyRequest = *request; + + burn (request->WipeKey, sizeof (request->WipeKey)); + + DecoySystemWipeThreadAbortRequested = FALSE; + KeInitializeSpinLock (&DecoySystemWipeStatusSpinLock); + DecoySystemWipedAreaEnd = BootDriveFilterExtension->ConfiguredEncryptedAreaStart; + + DecoySystemWipeInProgress = TRUE; + status = TCStartThread (DecoySystemWipeThreadProc, DeviceObject, &DecoySystemWipeThread); + + if (!NT_SUCCESS (status)) + DecoySystemWipeInProgress = FALSE; + + return status; +} + + +BOOL IsDecoySystemWipeInProgress() +{ + return DecoySystemWipeInProgress; +} + + +void GetDecoySystemWipeStatus (PIRP irp, PIO_STACK_LOCATION irpSp) +{ + if (ValidateIOBufferSize (irp, sizeof (DecoySystemWipeStatus), ValidateOutput)) + { + DecoySystemWipeStatus *wipeStatus = (DecoySystemWipeStatus *) irp->AssociatedIrp.SystemBuffer; + + if (!IsHiddenSystemRunning()) + { + irp->IoStatus.Status = STATUS_INVALID_PARAMETER; + irp->IoStatus.Information = 0; + } + else + { + wipeStatus->WipeInProgress = DecoySystemWipeInProgress; + wipeStatus->WipeAlgorithm = WipeDecoyRequest.WipeAlgorithm; + + if (DecoySystemWipeInProgress) + { + KIRQL irql; + KeAcquireSpinLock (&DecoySystemWipeStatusSpinLock, &irql); + wipeStatus->WipedAreaEnd = DecoySystemWipedAreaEnd; + KeReleaseSpinLock (&DecoySystemWipeStatusSpinLock, irql); + } + else + wipeStatus->WipedAreaEnd = DecoySystemWipedAreaEnd; + + irp->IoStatus.Information = sizeof (DecoySystemWipeStatus); + irp->IoStatus.Status = STATUS_SUCCESS; + } + } +} + + +NTSTATUS GetDecoySystemWipeResult() +{ + return DecoySystemWipeResult; +} + + +NTSTATUS AbortDecoySystemWipe () +{ + if (!IoIsSystemThread (PsGetCurrentThread()) && !UserCanAccessDriveDevice()) + return STATUS_ACCESS_DENIED; + + if (DecoySystemWipeThread) + { + DecoySystemWipeThreadAbortRequested = TRUE; + + TCStopThread (DecoySystemWipeThread, NULL); + DecoySystemWipeThread = NULL; + } + + return STATUS_SUCCESS; +} + + +uint64 GetBootDriveLength () +{ + return BootDriveLength.QuadPart; +} + + +NTSTATUS WriteBootDriveSector (PIRP irp, PIO_STACK_LOCATION irpSp) +{ + WriteBootDriveSectorRequest *request; + + if (!UserCanAccessDriveDevice()) + return STATUS_ACCESS_DENIED; + + if (!BootDriveFilterExtension + || irpSp->Parameters.DeviceIoControl.InputBufferLength < sizeof (WriteBootDriveSectorRequest)) + return STATUS_INVALID_PARAMETER; + + request = (WriteBootDriveSectorRequest *) irp->AssociatedIrp.SystemBuffer; + return TCWriteDevice (BootDriveFilterExtension->LowerDeviceObject, request->Data, request->Offset, sizeof (request->Data)); +} diff --git a/src/Driver/DriveFilter.h b/src/Driver/DriveFilter.h index 50dcabfd..e0c1bdff 100644 --- a/src/Driver/DriveFilter.h +++ b/src/Driver/DriveFilter.h @@ -1,90 +1,90 @@ -/* - Derived from source code of TrueCrypt 7.1a, which is - Copyright (c) 2008-2012 TrueCrypt Developers Association and which is governed - by the TrueCrypt License 3.0. - - Modifications and additions to the original source code (contained in this file) - and all other portions of this file are Copyright (c) 2013-2016 IDRIX - and are governed by the Apache License 2.0 the full text of which is - contained in the file License.txt included in VeraCrypt binary and source - code distribution packages. -*/ - -#ifndef TC_HEADER_DRIVER_DRIVE_FILTER -#define TC_HEADER_DRIVER_DRIVE_FILTER - -#include "TCdefs.h" -#include "Boot/Windows/BootCommon.h" -#include "EncryptedIoQueue.h" - -typedef struct _DriveFilterExtension -{ - BOOL bRootDevice; - BOOL IsVolumeDevice; - BOOL IsDriveFilterDevice; - BOOL IsVolumeFilterDevice; - uint64 MagicNumber; - - PDEVICE_OBJECT DeviceObject; - PDEVICE_OBJECT LowerDeviceObject; - PDEVICE_OBJECT Pdo; - - ULONG SystemStorageDeviceNumber; - BOOL SystemStorageDeviceNumberValid; - - int64 ConfiguredEncryptedAreaStart; - int64 ConfiguredEncryptedAreaEnd; - - uint32 VolumeHeaderSaltCrc32; - EncryptedIoQueue Queue; - - BOOL BootDrive; - BOOL VolumeHeaderPresent; - BOOL DriveMounted; - - KEVENT MountWorkItemCompletedEvent; - - CRYPTO_INFO *HeaderCryptoInfo; - BOOL HiddenSystem; - -} DriveFilterExtension; - -#define TC_BOOT_DRIVE_FILTER_EXTENSION_MAGIC_NUMBER 0x5645524142455854 - -extern BOOL BootArgsValid; -extern BootArguments BootArgs; -extern PKTHREAD EncryptionSetupThread; -extern PKTHREAD DecoySystemWipeThread; - -NTSTATUS AbortBootEncryptionSetup (); -NTSTATUS DriveFilterAddDevice (PDRIVER_OBJECT driverObject, PDEVICE_OBJECT pdo); -NTSTATUS DriveFilterDispatchIrp (PDEVICE_OBJECT DeviceObject, PIRP Irp); -void GetBootDriveVolumeProperties (PIRP irp, PIO_STACK_LOCATION irpSp); -void GetBootEncryptionAlgorithmName (PIRP irp, PIO_STACK_LOCATION irpSp); -void GetBootEncryptionStatus (PIRP irp, PIO_STACK_LOCATION irpSp); -void GetBootLoaderVersion (PIRP irp, PIO_STACK_LOCATION irpSp); -void GetBootLoaderFingerprint (PIRP irp, PIO_STACK_LOCATION irpSp); -NTSTATUS GetSetupResult (); -DriveFilterExtension *GetBootDriveFilterExtension (); -CRYPTO_INFO *GetSystemDriveCryptoInfo (); -BOOL IsBootDriveMounted (); -BOOL IsBootEncryptionSetupInProgress (); -BOOL IsHiddenSystemRunning (); -NTSTATUS LoadBootArguments (); -static NTSTATUS SaveDriveVolumeHeader (DriveFilterExtension *Extension); -NTSTATUS StartBootEncryptionSetup (PDEVICE_OBJECT DeviceObject, PIRP irp, PIO_STACK_LOCATION irpSp); -void ReopenBootVolumeHeader (PIRP irp, PIO_STACK_LOCATION irpSp); -NTSTATUS StartDecoySystemWipe (PDEVICE_OBJECT DeviceObject, PIRP irp, PIO_STACK_LOCATION irpSp); -void StartLegacyHibernationDriverFilter (); -NTSTATUS AbortDecoySystemWipe (); -BOOL IsDecoySystemWipeInProgress(); -NTSTATUS GetDecoySystemWipeResult(); -void GetDecoySystemWipeStatus (PIRP irp, PIO_STACK_LOCATION irpSp); -uint64 GetBootDriveLength (); -NTSTATUS WriteBootDriveSector (PIRP irp, PIO_STACK_LOCATION irpSp); - -#define TC_ENCRYPTION_SETUP_IO_BLOCK_SIZE (1536 * 1024) -#define TC_ENCRYPTION_SETUP_HEADER_UPDATE_THRESHOLD (64 * 1024 * 1024) -#define TC_HIBERNATION_WRITE_BUFFER_SIZE (128 * 1024) - -#endif // TC_HEADER_DRIVER_DRIVE_FILTER +/* + Derived from source code of TrueCrypt 7.1a, which is + Copyright (c) 2008-2012 TrueCrypt Developers Association and which is governed + by the TrueCrypt License 3.0. + + Modifications and additions to the original source code (contained in this file) + and all other portions of this file are Copyright (c) 2013-2016 IDRIX + and are governed by the Apache License 2.0 the full text of which is + contained in the file License.txt included in VeraCrypt binary and source + code distribution packages. +*/ + +#ifndef TC_HEADER_DRIVER_DRIVE_FILTER +#define TC_HEADER_DRIVER_DRIVE_FILTER + +#include "TCdefs.h" +#include "Boot/Windows/BootCommon.h" +#include "EncryptedIoQueue.h" + +typedef struct _DriveFilterExtension +{ + BOOL bRootDevice; + BOOL IsVolumeDevice; + BOOL IsDriveFilterDevice; + BOOL IsVolumeFilterDevice; + uint64 MagicNumber; + + PDEVICE_OBJECT DeviceObject; + PDEVICE_OBJECT LowerDeviceObject; + PDEVICE_OBJECT Pdo; + + ULONG SystemStorageDeviceNumber; + BOOL SystemStorageDeviceNumberValid; + + int64 ConfiguredEncryptedAreaStart; + int64 ConfiguredEncryptedAreaEnd; + + uint32 VolumeHeaderSaltCrc32; + EncryptedIoQueue Queue; + + BOOL BootDrive; + BOOL VolumeHeaderPresent; + BOOL DriveMounted; + + KEVENT MountWorkItemCompletedEvent; + + CRYPTO_INFO *HeaderCryptoInfo; + BOOL HiddenSystem; + +} DriveFilterExtension; + +#define TC_BOOT_DRIVE_FILTER_EXTENSION_MAGIC_NUMBER 0x5645524142455854 + +extern BOOL BootArgsValid; +extern BootArguments BootArgs; +extern PKTHREAD EncryptionSetupThread; +extern PKTHREAD DecoySystemWipeThread; + +NTSTATUS AbortBootEncryptionSetup (); +NTSTATUS DriveFilterAddDevice (PDRIVER_OBJECT driverObject, PDEVICE_OBJECT pdo); +NTSTATUS DriveFilterDispatchIrp (PDEVICE_OBJECT DeviceObject, PIRP Irp); +void GetBootDriveVolumeProperties (PIRP irp, PIO_STACK_LOCATION irpSp); +void GetBootEncryptionAlgorithmName (PIRP irp, PIO_STACK_LOCATION irpSp); +void GetBootEncryptionStatus (PIRP irp, PIO_STACK_LOCATION irpSp); +void GetBootLoaderVersion (PIRP irp, PIO_STACK_LOCATION irpSp); +void GetBootLoaderFingerprint (PIRP irp, PIO_STACK_LOCATION irpSp); +NTSTATUS GetSetupResult (); +DriveFilterExtension *GetBootDriveFilterExtension (); +CRYPTO_INFO *GetSystemDriveCryptoInfo (); +BOOL IsBootDriveMounted (); +BOOL IsBootEncryptionSetupInProgress (); +BOOL IsHiddenSystemRunning (); +NTSTATUS LoadBootArguments (); +static NTSTATUS SaveDriveVolumeHeader (DriveFilterExtension *Extension); +NTSTATUS StartBootEncryptionSetup (PDEVICE_OBJECT DeviceObject, PIRP irp, PIO_STACK_LOCATION irpSp); +void ReopenBootVolumeHeader (PIRP irp, PIO_STACK_LOCATION irpSp); +NTSTATUS StartDecoySystemWipe (PDEVICE_OBJECT DeviceObject, PIRP irp, PIO_STACK_LOCATION irpSp); +void StartLegacyHibernationDriverFilter (); +NTSTATUS AbortDecoySystemWipe (); +BOOL IsDecoySystemWipeInProgress(); +NTSTATUS GetDecoySystemWipeResult(); +void GetDecoySystemWipeStatus (PIRP irp, PIO_STACK_LOCATION irpSp); +uint64 GetBootDriveLength (); +NTSTATUS WriteBootDriveSector (PIRP irp, PIO_STACK_LOCATION irpSp); + +#define TC_ENCRYPTION_SETUP_IO_BLOCK_SIZE (1536 * 1024) +#define TC_ENCRYPTION_SETUP_HEADER_UPDATE_THRESHOLD (64 * 1024 * 1024) +#define TC_HIBERNATION_WRITE_BUFFER_SIZE (128 * 1024) + +#endif // TC_HEADER_DRIVER_DRIVE_FILTER diff --git a/src/Driver/Driver.rc b/src/Driver/Driver.rc index d9591d51..51030c5c 100644 --- a/src/Driver/Driver.rc +++ b/src/Driver/Driver.rc @@ -1,101 +1,101 @@ -// Microsoft Visual C++ generated resource script. -// -#include "resource.h" - -#define APSTUDIO_READONLY_SYMBOLS -///////////////////////////////////////////////////////////////////////////// -// -// Generated from the TEXTINCLUDE 2 resource. -// -#include "afxres.h" - -///////////////////////////////////////////////////////////////////////////// -#undef APSTUDIO_READONLY_SYMBOLS - -///////////////////////////////////////////////////////////////////////////// -// English (U.S.) resources - -#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) -#ifdef _WIN32 -LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US -#pragma code_page(1252) -#endif //_WIN32 - -///////////////////////////////////////////////////////////////////////////// -// -// Version -// - -VS_VERSION_INFO VERSIONINFO - FILEVERSION 1,17,22,0 - PRODUCTVERSION 1,17,22,0 - FILEFLAGSMASK 0x17L -#ifdef _DEBUG - FILEFLAGS 0x1L -#else - FILEFLAGS 0x0L -#endif - FILEOS 0x4L - FILETYPE 0x3L - FILESUBTYPE 0x0L -BEGIN - BLOCK "StringFileInfo" - BEGIN - BLOCK "040904b0" - BEGIN - VALUE "CompanyName", "IDRIX" - VALUE "FileDescription", "VeraCrypt Driver" - VALUE "FileVersion", "1.17" - VALUE "LegalTrademarks", "VeraCrypt" - VALUE "OriginalFilename", "veracrypt.sys" - VALUE "ProductName", "VeraCrypt" - VALUE "ProductVersion", "1.17" - END - END - BLOCK "VarFileInfo" - BEGIN - VALUE "Translation", 0x409, 1200 - END -END - - -#ifdef APSTUDIO_INVOKED -///////////////////////////////////////////////////////////////////////////// -// -// TEXTINCLUDE -// - -1 TEXTINCLUDE -BEGIN - "resource.h\0" -END - -2 TEXTINCLUDE -BEGIN - "#include ""afxres.h""\r\n" - "\0" -END - -3 TEXTINCLUDE -BEGIN - "\r\n" - "\0" -END - -#endif // APSTUDIO_INVOKED - -#endif // English (U.S.) resources -///////////////////////////////////////////////////////////////////////////// - - - -#ifndef APSTUDIO_INVOKED -///////////////////////////////////////////////////////////////////////////// -// -// Generated from the TEXTINCLUDE 3 resource. -// - - -///////////////////////////////////////////////////////////////////////////// -#endif // not APSTUDIO_INVOKED - +// Microsoft Visual C++ generated resource script. +// +#include "resource.h" + +#define APSTUDIO_READONLY_SYMBOLS +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 2 resource. +// +#include "afxres.h" + +///////////////////////////////////////////////////////////////////////////// +#undef APSTUDIO_READONLY_SYMBOLS + +///////////////////////////////////////////////////////////////////////////// +// English (U.S.) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) +#ifdef _WIN32 +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US +#pragma code_page(1252) +#endif //_WIN32 + +///////////////////////////////////////////////////////////////////////////// +// +// Version +// + +VS_VERSION_INFO VERSIONINFO + FILEVERSION 1,17,22,0 + PRODUCTVERSION 1,17,22,0 + FILEFLAGSMASK 0x17L +#ifdef _DEBUG + FILEFLAGS 0x1L +#else + FILEFLAGS 0x0L +#endif + FILEOS 0x4L + FILETYPE 0x3L + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904b0" + BEGIN + VALUE "CompanyName", "IDRIX" + VALUE "FileDescription", "VeraCrypt Driver" + VALUE "FileVersion", "1.17" + VALUE "LegalTrademarks", "VeraCrypt" + VALUE "OriginalFilename", "veracrypt.sys" + VALUE "ProductName", "VeraCrypt" + VALUE "ProductVersion", "1.17" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1200 + END +END + + +#ifdef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// TEXTINCLUDE +// + +1 TEXTINCLUDE +BEGIN + "resource.h\0" +END + +2 TEXTINCLUDE +BEGIN + "#include ""afxres.h""\r\n" + "\0" +END + +3 TEXTINCLUDE +BEGIN + "\r\n" + "\0" +END + +#endif // APSTUDIO_INVOKED + +#endif // English (U.S.) resources +///////////////////////////////////////////////////////////////////////////// + + + +#ifndef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 3 resource. +// + + +///////////////////////////////////////////////////////////////////////////// +#endif // not APSTUDIO_INVOKED + diff --git a/src/Driver/Driver.vcproj b/src/Driver/Driver.vcproj index 8d28d753..4c474c36 100644 --- a/src/Driver/Driver.vcproj +++ b/src/Driver/Driver.vcproj @@ -1,398 +1,398 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Driver/DumpFilter.c b/src/Driver/DumpFilter.c index 16d1d37c..d67b1654 100644 --- a/src/Driver/DumpFilter.c +++ b/src/Driver/DumpFilter.c @@ -1,248 +1,248 @@ -/* - Derived from source code of TrueCrypt 7.1a, which is - Copyright (c) 2008-2012 TrueCrypt Developers Association and which is governed - by the TrueCrypt License 3.0. - - Modifications and additions to the original source code (contained in this file) - and all other portions of this file are Copyright (c) 2013-2016 IDRIX - and are governed by the Apache License 2.0 the full text of which is - contained in the file License.txt included in VeraCrypt binary and source - code distribution packages. -*/ - -#include "DumpFilter.h" -#include "DriveFilter.h" -#include "Ntdriver.h" -#include "Tests.h" - -static DriveFilterExtension *BootDriveFilterExtension = NULL; -static LARGE_INTEGER DumpPartitionOffset; -static byte *WriteFilterBuffer = NULL; -static SIZE_T WriteFilterBufferSize; - - -NTSTATUS DumpFilterEntry (PFILTER_EXTENSION filterExtension, PFILTER_INITIALIZATION_DATA filterInitData) -{ - GetSystemDriveDumpConfigRequest dumpConfig; - PHYSICAL_ADDRESS highestAcceptableWriteBufferAddr; - STORAGE_DEVICE_NUMBER storageDeviceNumber; - PARTITION_INFORMATION partitionInfo; - LONG version; - NTSTATUS status; - - Dump ("DumpFilterEntry type=%d\n", filterExtension->DumpType); - - filterInitData->MajorVersion = DUMP_FILTER_MAJOR_VERSION; - filterInitData->MinorVersion = DUMP_FILTER_MINOR_VERSION; - filterInitData->Flags |= DUMP_FILTER_CRITICAL; - - // Check driver version of the main device - status = TCDeviceIoControl (NT_ROOT_PREFIX, TC_IOCTL_GET_DRIVER_VERSION, NULL, 0, &version, sizeof (version)); - if (!NT_SUCCESS (status)) - goto err; - - if (version != VERSION_NUM) - { - status = STATUS_INVALID_PARAMETER; - goto err; - } - - // Get dump configuration from the main device - status = TCDeviceIoControl (NT_ROOT_PREFIX, TC_IOCTL_GET_SYSTEM_DRIVE_DUMP_CONFIG, NULL, 0, &dumpConfig, sizeof (dumpConfig)); - if (!NT_SUCCESS (status)) - goto err; - - BootDriveFilterExtension = dumpConfig.BootDriveFilterExtension; - - if (BootDriveFilterExtension->MagicNumber != TC_BOOT_DRIVE_FILTER_EXTENSION_MAGIC_NUMBER) - { - status = STATUS_CRC_ERROR; - goto err; - } - - // KeSaveFloatingPointState() may generate a bug check during crash dump -#if !defined (_WIN64) - if (filterExtension->DumpType == DumpTypeCrashdump) - dumpConfig.HwEncryptionEnabled = FALSE; -#endif - - EnableHwEncryption (dumpConfig.HwEncryptionEnabled); - - if (!AutoTestAlgorithms()) - { - status = STATUS_INVALID_PARAMETER; - goto err; - } - - // Check dump volume is located on the system drive - status = SendDeviceIoControlRequest (filterExtension->DeviceObject, IOCTL_STORAGE_GET_DEVICE_NUMBER, NULL, 0, &storageDeviceNumber, sizeof (storageDeviceNumber)); - if (!NT_SUCCESS (status)) - goto err; - - if (!BootDriveFilterExtension->SystemStorageDeviceNumberValid) - { - status = STATUS_INVALID_PARAMETER; - goto err; - } - - if (storageDeviceNumber.DeviceNumber != BootDriveFilterExtension->SystemStorageDeviceNumber) - { - status = STATUS_ACCESS_DENIED; - goto err; - } - - // Check dump volume is located within the scope of system encryption - status = SendDeviceIoControlRequest (filterExtension->DeviceObject, IOCTL_DISK_GET_PARTITION_INFO, NULL, 0, &partitionInfo, sizeof (partitionInfo)); - if (!NT_SUCCESS (status)) - goto err; - - DumpPartitionOffset = partitionInfo.StartingOffset; - - if (DumpPartitionOffset.QuadPart < BootDriveFilterExtension->ConfiguredEncryptedAreaStart - || DumpPartitionOffset.QuadPart > BootDriveFilterExtension->ConfiguredEncryptedAreaEnd) - { - status = STATUS_ACCESS_DENIED; - goto err; - } - - // Allocate buffer for encryption - if (filterInitData->MaxPagesPerWrite == 0) - { - status = STATUS_INVALID_PARAMETER; - goto err; - } - - WriteFilterBufferSize = filterInitData->MaxPagesPerWrite * PAGE_SIZE; - -#ifdef _WIN64 - highestAcceptableWriteBufferAddr.QuadPart = 0x7FFffffFFFFLL; -#else - highestAcceptableWriteBufferAddr.QuadPart = 0xffffFFFFLL; -#endif - - WriteFilterBuffer = MmAllocateContiguousMemory (WriteFilterBufferSize, highestAcceptableWriteBufferAddr); - if (!WriteFilterBuffer) - { - status = STATUS_INSUFFICIENT_RESOURCES; - goto err; - } - - filterInitData->DumpStart = DumpFilterStart; - filterInitData->DumpWrite = DumpFilterWrite; - filterInitData->DumpFinish = DumpFilterFinish; - filterInitData->DumpUnload = DumpFilterUnload; - - Dump ("Dump filter loaded type=%d\n", filterExtension->DumpType); - return STATUS_SUCCESS; - -err: - Dump ("DumpFilterEntry error %x\n", status); - return status; -} - - -static NTSTATUS DumpFilterStart (PFILTER_EXTENSION filterExtension) -{ - Dump ("DumpFilterStart type=%d\n", filterExtension->DumpType); - - if (BootDriveFilterExtension->MagicNumber != TC_BOOT_DRIVE_FILTER_EXTENSION_MAGIC_NUMBER) - TC_BUG_CHECK (STATUS_CRC_ERROR); - - return BootDriveFilterExtension->DriveMounted ? STATUS_SUCCESS : STATUS_UNSUCCESSFUL; -} - - -static NTSTATUS DumpFilterWrite (PFILTER_EXTENSION filterExtension, PLARGE_INTEGER diskWriteOffset, PMDL writeMdl) -{ - ULONG dataLength = MmGetMdlByteCount (writeMdl); - uint64 offset = DumpPartitionOffset.QuadPart + diskWriteOffset->QuadPart; - uint64 intersectStart; - uint32 intersectLength; - PVOID writeBuffer; - CSHORT origMdlFlags; - - if (BootDriveFilterExtension->MagicNumber != TC_BOOT_DRIVE_FILTER_EXTENSION_MAGIC_NUMBER) - TC_BUG_CHECK (STATUS_CRC_ERROR); - - if (BootDriveFilterExtension->Queue.EncryptedAreaEndUpdatePending) // Hibernation should always abort the setup thread - TC_BUG_CHECK (STATUS_INVALID_PARAMETER); - - if (BootDriveFilterExtension->Queue.EncryptedAreaStart == -1 || BootDriveFilterExtension->Queue.EncryptedAreaEnd == -1) - return STATUS_SUCCESS; - - if (dataLength > WriteFilterBufferSize) - TC_BUG_CHECK (STATUS_BUFFER_OVERFLOW); // Bug check is required as returning an error does not prevent data from being written to disk - - if ((dataLength & (ENCRYPTION_DATA_UNIT_SIZE - 1)) != 0) - TC_BUG_CHECK (STATUS_INVALID_PARAMETER); - - if ((offset & (ENCRYPTION_DATA_UNIT_SIZE - 1)) != 0) - TC_BUG_CHECK (STATUS_INVALID_PARAMETER); - - writeBuffer = MmGetSystemAddressForMdlSafe (writeMdl, HighPagePriority); - if (!writeBuffer) - TC_BUG_CHECK (STATUS_INSUFFICIENT_RESOURCES); - - memcpy (WriteFilterBuffer, writeBuffer, dataLength); - - GetIntersection (offset, - dataLength, - BootDriveFilterExtension->Queue.EncryptedAreaStart, - BootDriveFilterExtension->Queue.EncryptedAreaEnd, - &intersectStart, - &intersectLength); - - if (intersectLength > 0) - { - UINT64_STRUCT dataUnit; - dataUnit.Value = intersectStart / ENCRYPTION_DATA_UNIT_SIZE; - - if (BootDriveFilterExtension->Queue.RemapEncryptedArea) - { - diskWriteOffset->QuadPart += BootDriveFilterExtension->Queue.RemappedAreaOffset; - dataUnit.Value += BootDriveFilterExtension->Queue.RemappedAreaDataUnitOffset; - } - - EncryptDataUnitsCurrentThread (WriteFilterBuffer + (intersectStart - offset), - &dataUnit, - intersectLength / ENCRYPTION_DATA_UNIT_SIZE, - BootDriveFilterExtension->Queue.CryptoInfo); - } - - origMdlFlags = writeMdl->MdlFlags; - - MmInitializeMdl (writeMdl, WriteFilterBuffer, dataLength); - MmBuildMdlForNonPagedPool (writeMdl); - - // Instead of using MmGetSystemAddressForMdlSafe(), some buggy custom storage drivers may directly test MDL_MAPPED_TO_SYSTEM_VA flag, - // disregarding the fact that other MDL flags may be set by the system or a dump filter (e.g. MDL_SOURCE_IS_NONPAGED_POOL flag only). - // Therefore, to work around this issue, the original flags will be restored even if they do not match the new MDL. - // MS BitLocker also uses this hack/workaround (it should be safe to use until the MDL structure is changed). - - writeMdl->MdlFlags = origMdlFlags; - - return STATUS_SUCCESS; -} - - -static NTSTATUS DumpFilterFinish (PFILTER_EXTENSION filterExtension) -{ - Dump ("DumpFilterFinish type=%d\n", filterExtension->DumpType); - - return STATUS_SUCCESS; -} - - -static NTSTATUS DumpFilterUnload (PFILTER_EXTENSION filterExtension) -{ - Dump ("DumpFilterUnload type=%d\n", filterExtension->DumpType); - - if (WriteFilterBuffer) - { - memset (WriteFilterBuffer, 0, WriteFilterBufferSize); - MmFreeContiguousMemory (WriteFilterBuffer); - WriteFilterBuffer = NULL; - } - - return STATUS_SUCCESS; -} +/* + Derived from source code of TrueCrypt 7.1a, which is + Copyright (c) 2008-2012 TrueCrypt Developers Association and which is governed + by the TrueCrypt License 3.0. + + Modifications and additions to the original source code (contained in this file) + and all other portions of this file are Copyright (c) 2013-2016 IDRIX + and are governed by the Apache License 2.0 the full text of which is + contained in the file License.txt included in VeraCrypt binary and source + code distribution packages. +*/ + +#include "DumpFilter.h" +#include "DriveFilter.h" +#include "Ntdriver.h" +#include "Tests.h" + +static DriveFilterExtension *BootDriveFilterExtension = NULL; +static LARGE_INTEGER DumpPartitionOffset; +static byte *WriteFilterBuffer = NULL; +static SIZE_T WriteFilterBufferSize; + + +NTSTATUS DumpFilterEntry (PFILTER_EXTENSION filterExtension, PFILTER_INITIALIZATION_DATA filterInitData) +{ + GetSystemDriveDumpConfigRequest dumpConfig; + PHYSICAL_ADDRESS highestAcceptableWriteBufferAddr; + STORAGE_DEVICE_NUMBER storageDeviceNumber; + PARTITION_INFORMATION partitionInfo; + LONG version; + NTSTATUS status; + + Dump ("DumpFilterEntry type=%d\n", filterExtension->DumpType); + + filterInitData->MajorVersion = DUMP_FILTER_MAJOR_VERSION; + filterInitData->MinorVersion = DUMP_FILTER_MINOR_VERSION; + filterInitData->Flags |= DUMP_FILTER_CRITICAL; + + // Check driver version of the main device + status = TCDeviceIoControl (NT_ROOT_PREFIX, TC_IOCTL_GET_DRIVER_VERSION, NULL, 0, &version, sizeof (version)); + if (!NT_SUCCESS (status)) + goto err; + + if (version != VERSION_NUM) + { + status = STATUS_INVALID_PARAMETER; + goto err; + } + + // Get dump configuration from the main device + status = TCDeviceIoControl (NT_ROOT_PREFIX, TC_IOCTL_GET_SYSTEM_DRIVE_DUMP_CONFIG, NULL, 0, &dumpConfig, sizeof (dumpConfig)); + if (!NT_SUCCESS (status)) + goto err; + + BootDriveFilterExtension = dumpConfig.BootDriveFilterExtension; + + if (BootDriveFilterExtension->MagicNumber != TC_BOOT_DRIVE_FILTER_EXTENSION_MAGIC_NUMBER) + { + status = STATUS_CRC_ERROR; + goto err; + } + + // KeSaveFloatingPointState() may generate a bug check during crash dump +#if !defined (_WIN64) + if (filterExtension->DumpType == DumpTypeCrashdump) + dumpConfig.HwEncryptionEnabled = FALSE; +#endif + + EnableHwEncryption (dumpConfig.HwEncryptionEnabled); + + if (!AutoTestAlgorithms()) + { + status = STATUS_INVALID_PARAMETER; + goto err; + } + + // Check dump volume is located on the system drive + status = SendDeviceIoControlRequest (filterExtension->DeviceObject, IOCTL_STORAGE_GET_DEVICE_NUMBER, NULL, 0, &storageDeviceNumber, sizeof (storageDeviceNumber)); + if (!NT_SUCCESS (status)) + goto err; + + if (!BootDriveFilterExtension->SystemStorageDeviceNumberValid) + { + status = STATUS_INVALID_PARAMETER; + goto err; + } + + if (storageDeviceNumber.DeviceNumber != BootDriveFilterExtension->SystemStorageDeviceNumber) + { + status = STATUS_ACCESS_DENIED; + goto err; + } + + // Check dump volume is located within the scope of system encryption + status = SendDeviceIoControlRequest (filterExtension->DeviceObject, IOCTL_DISK_GET_PARTITION_INFO, NULL, 0, &partitionInfo, sizeof (partitionInfo)); + if (!NT_SUCCESS (status)) + goto err; + + DumpPartitionOffset = partitionInfo.StartingOffset; + + if (DumpPartitionOffset.QuadPart < BootDriveFilterExtension->ConfiguredEncryptedAreaStart + || DumpPartitionOffset.QuadPart > BootDriveFilterExtension->ConfiguredEncryptedAreaEnd) + { + status = STATUS_ACCESS_DENIED; + goto err; + } + + // Allocate buffer for encryption + if (filterInitData->MaxPagesPerWrite == 0) + { + status = STATUS_INVALID_PARAMETER; + goto err; + } + + WriteFilterBufferSize = filterInitData->MaxPagesPerWrite * PAGE_SIZE; + +#ifdef _WIN64 + highestAcceptableWriteBufferAddr.QuadPart = 0x7FFffffFFFFLL; +#else + highestAcceptableWriteBufferAddr.QuadPart = 0xffffFFFFLL; +#endif + + WriteFilterBuffer = MmAllocateContiguousMemory (WriteFilterBufferSize, highestAcceptableWriteBufferAddr); + if (!WriteFilterBuffer) + { + status = STATUS_INSUFFICIENT_RESOURCES; + goto err; + } + + filterInitData->DumpStart = DumpFilterStart; + filterInitData->DumpWrite = DumpFilterWrite; + filterInitData->DumpFinish = DumpFilterFinish; + filterInitData->DumpUnload = DumpFilterUnload; + + Dump ("Dump filter loaded type=%d\n", filterExtension->DumpType); + return STATUS_SUCCESS; + +err: + Dump ("DumpFilterEntry error %x\n", status); + return status; +} + + +static NTSTATUS DumpFilterStart (PFILTER_EXTENSION filterExtension) +{ + Dump ("DumpFilterStart type=%d\n", filterExtension->DumpType); + + if (BootDriveFilterExtension->MagicNumber != TC_BOOT_DRIVE_FILTER_EXTENSION_MAGIC_NUMBER) + TC_BUG_CHECK (STATUS_CRC_ERROR); + + return BootDriveFilterExtension->DriveMounted ? STATUS_SUCCESS : STATUS_UNSUCCESSFUL; +} + + +static NTSTATUS DumpFilterWrite (PFILTER_EXTENSION filterExtension, PLARGE_INTEGER diskWriteOffset, PMDL writeMdl) +{ + ULONG dataLength = MmGetMdlByteCount (writeMdl); + uint64 offset = DumpPartitionOffset.QuadPart + diskWriteOffset->QuadPart; + uint64 intersectStart; + uint32 intersectLength; + PVOID writeBuffer; + CSHORT origMdlFlags; + + if (BootDriveFilterExtension->MagicNumber != TC_BOOT_DRIVE_FILTER_EXTENSION_MAGIC_NUMBER) + TC_BUG_CHECK (STATUS_CRC_ERROR); + + if (BootDriveFilterExtension->Queue.EncryptedAreaEndUpdatePending) // Hibernation should always abort the setup thread + TC_BUG_CHECK (STATUS_INVALID_PARAMETER); + + if (BootDriveFilterExtension->Queue.EncryptedAreaStart == -1 || BootDriveFilterExtension->Queue.EncryptedAreaEnd == -1) + return STATUS_SUCCESS; + + if (dataLength > WriteFilterBufferSize) + TC_BUG_CHECK (STATUS_BUFFER_OVERFLOW); // Bug check is required as returning an error does not prevent data from being written to disk + + if ((dataLength & (ENCRYPTION_DATA_UNIT_SIZE - 1)) != 0) + TC_BUG_CHECK (STATUS_INVALID_PARAMETER); + + if ((offset & (ENCRYPTION_DATA_UNIT_SIZE - 1)) != 0) + TC_BUG_CHECK (STATUS_INVALID_PARAMETER); + + writeBuffer = MmGetSystemAddressForMdlSafe (writeMdl, HighPagePriority); + if (!writeBuffer) + TC_BUG_CHECK (STATUS_INSUFFICIENT_RESOURCES); + + memcpy (WriteFilterBuffer, writeBuffer, dataLength); + + GetIntersection (offset, + dataLength, + BootDriveFilterExtension->Queue.EncryptedAreaStart, + BootDriveFilterExtension->Queue.EncryptedAreaEnd, + &intersectStart, + &intersectLength); + + if (intersectLength > 0) + { + UINT64_STRUCT dataUnit; + dataUnit.Value = intersectStart / ENCRYPTION_DATA_UNIT_SIZE; + + if (BootDriveFilterExtension->Queue.RemapEncryptedArea) + { + diskWriteOffset->QuadPart += BootDriveFilterExtension->Queue.RemappedAreaOffset; + dataUnit.Value += BootDriveFilterExtension->Queue.RemappedAreaDataUnitOffset; + } + + EncryptDataUnitsCurrentThread (WriteFilterBuffer + (intersectStart - offset), + &dataUnit, + intersectLength / ENCRYPTION_DATA_UNIT_SIZE, + BootDriveFilterExtension->Queue.CryptoInfo); + } + + origMdlFlags = writeMdl->MdlFlags; + + MmInitializeMdl (writeMdl, WriteFilterBuffer, dataLength); + MmBuildMdlForNonPagedPool (writeMdl); + + // Instead of using MmGetSystemAddressForMdlSafe(), some buggy custom storage drivers may directly test MDL_MAPPED_TO_SYSTEM_VA flag, + // disregarding the fact that other MDL flags may be set by the system or a dump filter (e.g. MDL_SOURCE_IS_NONPAGED_POOL flag only). + // Therefore, to work around this issue, the original flags will be restored even if they do not match the new MDL. + // MS BitLocker also uses this hack/workaround (it should be safe to use until the MDL structure is changed). + + writeMdl->MdlFlags = origMdlFlags; + + return STATUS_SUCCESS; +} + + +static NTSTATUS DumpFilterFinish (PFILTER_EXTENSION filterExtension) +{ + Dump ("DumpFilterFinish type=%d\n", filterExtension->DumpType); + + return STATUS_SUCCESS; +} + + +static NTSTATUS DumpFilterUnload (PFILTER_EXTENSION filterExtension) +{ + Dump ("DumpFilterUnload type=%d\n", filterExtension->DumpType); + + if (WriteFilterBuffer) + { + memset (WriteFilterBuffer, 0, WriteFilterBufferSize); + MmFreeContiguousMemory (WriteFilterBuffer); + WriteFilterBuffer = NULL; + } + + return STATUS_SUCCESS; +} diff --git a/src/Driver/DumpFilter.h b/src/Driver/DumpFilter.h index f288107e..ea6599a5 100644 --- a/src/Driver/DumpFilter.h +++ b/src/Driver/DumpFilter.h @@ -1,25 +1,25 @@ -/* - Derived from source code of TrueCrypt 7.1a, which is - Copyright (c) 2008-2012 TrueCrypt Developers Association and which is governed - by the TrueCrypt License 3.0. - - Modifications and additions to the original source code (contained in this file) - and all other portions of this file are Copyright (c) 2013-2016 IDRIX - and are governed by the Apache License 2.0 the full text of which is - contained in the file License.txt included in VeraCrypt binary and source - code distribution packages. -*/ - -#ifndef TC_HEADER_DRIVER_DUMP_FILTER -#define TC_HEADER_DRIVER_DUMP_FILTER - -#include "Tcdefs.h" -#include - -NTSTATUS DumpFilterEntry (PFILTER_EXTENSION filterExtension, PFILTER_INITIALIZATION_DATA filterInitData); -static NTSTATUS DumpFilterStart (PFILTER_EXTENSION filterExtension); -static NTSTATUS DumpFilterWrite (PFILTER_EXTENSION filterExtension, PLARGE_INTEGER diskWriteOffset, PMDL writeMdl); -static NTSTATUS DumpFilterFinish (PFILTER_EXTENSION filterExtension); -static NTSTATUS DumpFilterUnload (PFILTER_EXTENSION filterExtension); - -#endif // TC_HEADER_DRIVER_DUMP_FILTER +/* + Derived from source code of TrueCrypt 7.1a, which is + Copyright (c) 2008-2012 TrueCrypt Developers Association and which is governed + by the TrueCrypt License 3.0. + + Modifications and additions to the original source code (contained in this file) + and all other portions of this file are Copyright (c) 2013-2016 IDRIX + and are governed by the Apache License 2.0 the full text of which is + contained in the file License.txt included in VeraCrypt binary and source + code distribution packages. +*/ + +#ifndef TC_HEADER_DRIVER_DUMP_FILTER +#define TC_HEADER_DRIVER_DUMP_FILTER + +#include "Tcdefs.h" +#include + +NTSTATUS DumpFilterEntry (PFILTER_EXTENSION filterExtension, PFILTER_INITIALIZATION_DATA filterInitData); +static NTSTATUS DumpFilterStart (PFILTER_EXTENSION filterExtension); +static NTSTATUS DumpFilterWrite (PFILTER_EXTENSION filterExtension, PLARGE_INTEGER diskWriteOffset, PMDL writeMdl); +static NTSTATUS DumpFilterFinish (PFILTER_EXTENSION filterExtension); +static NTSTATUS DumpFilterUnload (PFILTER_EXTENSION filterExtension); + +#endif // TC_HEADER_DRIVER_DUMP_FILTER diff --git a/src/Driver/EncryptedIoQueue.c b/src/Driver/EncryptedIoQueue.c index 1f57ad5c..637f8504 100644 --- a/src/Driver/EncryptedIoQueue.c +++ b/src/Driver/EncryptedIoQueue.c @@ -1,1049 +1,1049 @@ -/* - Derived from source code of TrueCrypt 7.1a, which is - Copyright (c) 2008-2012 TrueCrypt Developers Association and which is governed - by the TrueCrypt License 3.0. - - Modifications and additions to the original source code (contained in this file) - and all other portions of this file are Copyright (c) 2013-2016 IDRIX - and are governed by the Apache License 2.0 the full text of which is - contained in the file License.txt included in VeraCrypt binary and source - code distribution packages. -*/ - -#include "TCdefs.h" -#include "Apidrvr.h" -#include "Ntdriver.h" -#include "DriveFilter.h" -#include "EncryptedIoQueue.h" -#include "EncryptionThreadPool.h" -#include "Volumes.h" -#include - - -static void AcquireBufferPoolMutex (EncryptedIoQueue *queue) -{ - NTSTATUS status; - - status = KeWaitForMutexObject (&queue->BufferPoolMutex, Executive, KernelMode, FALSE, NULL); - if (!NT_SUCCESS (status)) - TC_BUG_CHECK (status); -} - - -static void ReleaseBufferPoolMutex (EncryptedIoQueue *queue) -{ - KeReleaseMutex (&queue->BufferPoolMutex, FALSE); -} - - -static void *GetPoolBuffer (EncryptedIoQueue *queue, ULONG requestedSize) -{ - EncryptedIoQueueBuffer *buffer; - void *bufferAddress = NULL; - BOOL requestedSizePresentInPool = FALSE; - - while (TRUE) - { - AcquireBufferPoolMutex (queue); - - for (buffer = queue->FirstPoolBuffer; ; buffer = buffer->NextBuffer) - { - if (buffer && buffer->Size == requestedSize) - { - requestedSizePresentInPool = TRUE; - - if (!buffer->InUse) - { - // Reuse a free buffer - buffer->InUse = TRUE; - bufferAddress = buffer->Address; - break; - } - } - - if (!buffer || !buffer->NextBuffer) - { - EncryptedIoQueueBuffer *newBuffer; - - if (requestedSizePresentInPool && !queue->StartPending) - break; - - // Allocate a new buffer - newBuffer = TCalloc (sizeof (EncryptedIoQueueBuffer)); - if (!newBuffer) - { - bufferAddress = NULL; - break; - } - - bufferAddress = TCalloc (requestedSize); - if (bufferAddress) - { - newBuffer->NextBuffer = NULL; - newBuffer->Address = bufferAddress; - newBuffer->Size = requestedSize; - newBuffer->InUse = TRUE; - - if (!buffer) - queue->FirstPoolBuffer = newBuffer; - else - buffer->NextBuffer = newBuffer; - } - else - TCfree (newBuffer); - - break; - } - } - - ReleaseBufferPoolMutex (queue); - - if (bufferAddress || !requestedSizePresentInPool || queue->StartPending) - break; - - KeWaitForSingleObject (&queue->PoolBufferFreeEvent, Executive, KernelMode, FALSE, NULL); - } - - return bufferAddress; -} - - -static void ReleasePoolBuffer (EncryptedIoQueue *queue, void *address) -{ - EncryptedIoQueueBuffer *buffer; - AcquireBufferPoolMutex (queue); - - for (buffer = queue->FirstPoolBuffer; buffer != NULL; buffer = buffer->NextBuffer) - { - if (buffer->Address == address) - { - ASSERT (buffer->InUse); - - buffer->InUse = FALSE; - break; - } - } - - ReleaseBufferPoolMutex (queue); - KeSetEvent (&queue->PoolBufferFreeEvent, IO_DISK_INCREMENT, FALSE); -} - - -static void FreePoolBuffers (EncryptedIoQueue *queue) -{ - EncryptedIoQueueBuffer *buffer; - AcquireBufferPoolMutex (queue); - - for (buffer = queue->FirstPoolBuffer; buffer != NULL; ) - { - EncryptedIoQueueBuffer *nextBuffer = buffer->NextBuffer; - - ASSERT (!buffer->InUse || queue->StartPending); - - TCfree (buffer->Address); - TCfree (buffer); - - buffer = nextBuffer; - } - - queue->FirstPoolBuffer = NULL; - ReleaseBufferPoolMutex (queue); -} - - -static void DecrementOutstandingIoCount (EncryptedIoQueue *queue) -{ - if (InterlockedDecrement (&queue->OutstandingIoCount) == 0 && (queue->SuspendPending || queue->StopPending)) - KeSetEvent (&queue->NoOutstandingIoEvent, IO_DISK_INCREMENT, FALSE); -} - - -static void OnItemCompleted (EncryptedIoQueueItem *item, BOOL freeItem) -{ - DecrementOutstandingIoCount (item->Queue); - IoReleaseRemoveLock (&item->Queue->RemoveLock, item->OriginalIrp); - - if (NT_SUCCESS (item->Status)) - { - if (item->Write) - item->Queue->TotalBytesWritten += item->OriginalLength; - else - item->Queue->TotalBytesRead += item->OriginalLength; - } - - if (freeItem) - ReleasePoolBuffer (item->Queue, item); -} - - -static NTSTATUS CompleteOriginalIrp (EncryptedIoQueueItem *item, NTSTATUS status, ULONG_PTR information) -{ -#ifdef TC_TRACE_IO_QUEUE - Dump ("< %I64d [%I64d] %c status=%x info=%I64d\n", item->OriginalIrpOffset, GetElapsedTime (&item->Queue->LastPerformanceCounter), item->Write ? 'W' : 'R', status, (int64) information); -#endif - - TCCompleteDiskIrp (item->OriginalIrp, status, information); - - item->Status = status; - OnItemCompleted (item, TRUE); - - return status; -} - - -static void AcquireFragmentBuffer (EncryptedIoQueue *queue, byte *buffer) -{ - NTSTATUS status = STATUS_INVALID_PARAMETER; - - if (buffer == queue->FragmentBufferA) - { - status = KeWaitForSingleObject (&queue->FragmentBufferAFreeEvent, Executive, KernelMode, FALSE, NULL); - } - else if (buffer == queue->FragmentBufferB) - { - status = KeWaitForSingleObject (&queue->FragmentBufferBFreeEvent, Executive, KernelMode, FALSE, NULL); - } - - if (!NT_SUCCESS (status)) - TC_BUG_CHECK (status); -} - - -static void ReleaseFragmentBuffer (EncryptedIoQueue *queue, byte *buffer) -{ - if (buffer == queue->FragmentBufferA) - { - KeSetEvent (&queue->FragmentBufferAFreeEvent, IO_DISK_INCREMENT, FALSE); - } - else if (buffer == queue->FragmentBufferB) - { - KeSetEvent (&queue->FragmentBufferBFreeEvent, IO_DISK_INCREMENT, FALSE); - } - else - { - TC_BUG_CHECK (STATUS_INVALID_PARAMETER); - } -} - - -static VOID CompletionThreadProc (PVOID threadArg) -{ - EncryptedIoQueue *queue = (EncryptedIoQueue *) threadArg; - PLIST_ENTRY listEntry; - EncryptedIoRequest *request; - UINT64_STRUCT dataUnit; - - if (IsEncryptionThreadPoolRunning()) - KeSetPriorityThread (KeGetCurrentThread(), LOW_REALTIME_PRIORITY); - - while (!queue->ThreadExitRequested) - { - if (!NT_SUCCESS (KeWaitForSingleObject (&queue->CompletionThreadQueueNotEmptyEvent, Executive, KernelMode, FALSE, NULL))) - continue; - - if (queue->ThreadExitRequested) - break; - - while ((listEntry = ExInterlockedRemoveHeadList (&queue->CompletionThreadQueue, &queue->CompletionThreadQueueLock))) - { - request = CONTAINING_RECORD (listEntry, EncryptedIoRequest, CompletionListEntry); - - if (request->EncryptedLength > 0 && NT_SUCCESS (request->Item->Status)) - { - ASSERT (request->EncryptedOffset + request->EncryptedLength <= request->Offset.QuadPart + request->Length); - dataUnit.Value = (request->Offset.QuadPart + request->EncryptedOffset) / ENCRYPTION_DATA_UNIT_SIZE; - - if (queue->CryptoInfo->bPartitionInInactiveSysEncScope) - dataUnit.Value += queue->CryptoInfo->FirstDataUnitNo.Value; - else if (queue->RemapEncryptedArea) - dataUnit.Value += queue->RemappedAreaDataUnitOffset; - - DecryptDataUnits (request->Data + request->EncryptedOffset, &dataUnit, request->EncryptedLength / ENCRYPTION_DATA_UNIT_SIZE, queue->CryptoInfo); - } - - if (request->CompleteOriginalIrp) - { - CompleteOriginalIrp (request->Item, request->Item->Status, - NT_SUCCESS (request->Item->Status) ? request->Item->OriginalLength : 0); - } - - ReleasePoolBuffer (queue, request); - } - } - - PsTerminateSystemThread (STATUS_SUCCESS); -} - - -static NTSTATUS TCCachedRead (EncryptedIoQueue *queue, IO_STATUS_BLOCK *ioStatus, PVOID buffer, LARGE_INTEGER offset, ULONG length) -{ - queue->LastReadOffset = offset; - queue->LastReadLength = length; - - if (queue->ReadAheadBufferValid && queue->ReadAheadOffset.QuadPart == offset.QuadPart && queue->ReadAheadLength >= length) - { - memcpy (buffer, queue->ReadAheadBuffer, length); - - if (!queue->IsFilterDevice) - { - ioStatus->Information = length; - ioStatus->Status = STATUS_SUCCESS; - } - - return STATUS_SUCCESS; - } - - if (queue->IsFilterDevice) - return TCReadDevice (queue->LowerDeviceObject, buffer, offset, length); - - return ZwReadFile (queue->HostFileHandle, NULL, NULL, NULL, ioStatus, buffer, length, &offset, NULL); -} - - -static VOID IoThreadProc (PVOID threadArg) -{ - EncryptedIoQueue *queue = (EncryptedIoQueue *) threadArg; - PLIST_ENTRY listEntry; - EncryptedIoRequest *request; - - KeSetPriorityThread (KeGetCurrentThread(), LOW_REALTIME_PRIORITY); - - if (!queue->IsFilterDevice && queue->SecurityClientContext) - { -#ifdef DEBUG - NTSTATUS status = -#endif - SeImpersonateClientEx (queue->SecurityClientContext, NULL); - ASSERT (NT_SUCCESS (status)); - } - - while (!queue->ThreadExitRequested) - { - if (!NT_SUCCESS (KeWaitForSingleObject (&queue->IoThreadQueueNotEmptyEvent, Executive, KernelMode, FALSE, NULL))) - continue; - - if (queue->ThreadExitRequested) - break; - - while ((listEntry = ExInterlockedRemoveHeadList (&queue->IoThreadQueue, &queue->IoThreadQueueLock))) - { - InterlockedDecrement (&queue->IoThreadPendingRequestCount); - request = CONTAINING_RECORD (listEntry, EncryptedIoRequest, ListEntry); - -#ifdef TC_TRACE_IO_QUEUE - Dump ("%c %I64d [%I64d] roff=%I64d rlen=%d\n", request->Item->Write ? 'W' : 'R', request->Item->OriginalIrpOffset.QuadPart, GetElapsedTime (&queue->LastPerformanceCounter), request->Offset.QuadPart, request->Length); -#endif - - // Perform IO request if no preceding request of the item failed - if (NT_SUCCESS (request->Item->Status)) - { - if (queue->IsFilterDevice) - { - if (queue->RemapEncryptedArea && request->EncryptedLength > 0) - { - if (request->EncryptedLength != request->Length) - { - // Up to three subfragments may be required to handle a partially remapped fragment - int subFragment; - byte *subFragmentData = request->Data; - - for (subFragment = 0 ; subFragment < 3; ++subFragment) - { - LARGE_INTEGER subFragmentOffset; - ULONG subFragmentLength; - subFragmentOffset.QuadPart = request->Offset.QuadPart; - - switch (subFragment) - { - case 0: - subFragmentLength = (ULONG) request->EncryptedOffset; - break; - - case 1: - subFragmentOffset.QuadPart += request->EncryptedOffset + queue->RemappedAreaOffset; - subFragmentLength = request->EncryptedLength; - break; - - case 2: - subFragmentOffset.QuadPart += request->EncryptedOffset + request->EncryptedLength; - subFragmentLength = (ULONG) (request->Length - (request->EncryptedOffset + request->EncryptedLength)); - break; - } - - if (subFragmentLength > 0) - { - if (request->Item->Write) - request->Item->Status = TCWriteDevice (queue->LowerDeviceObject, subFragmentData, subFragmentOffset, subFragmentLength); - else - request->Item->Status = TCCachedRead (queue, NULL, subFragmentData, subFragmentOffset, subFragmentLength); - - subFragmentData += subFragmentLength; - } - } - } - else - { - // Remap the fragment - LARGE_INTEGER remappedOffset; - remappedOffset.QuadPart = request->Offset.QuadPart + queue->RemappedAreaOffset; - - if (request->Item->Write) - request->Item->Status = TCWriteDevice (queue->LowerDeviceObject, request->Data, remappedOffset, request->Length); - else - request->Item->Status = TCCachedRead (queue, NULL, request->Data, remappedOffset, request->Length); - } - } - else - { - if (request->Item->Write) - request->Item->Status = TCWriteDevice (queue->LowerDeviceObject, request->Data, request->Offset, request->Length); - else - request->Item->Status = TCCachedRead (queue, NULL, request->Data, request->Offset, request->Length); - } - } - else - { - IO_STATUS_BLOCK ioStatus; - - if (request->Item->Write) - request->Item->Status = ZwWriteFile (queue->HostFileHandle, NULL, NULL, NULL, &ioStatus, request->Data, request->Length, &request->Offset, NULL); - else - request->Item->Status = TCCachedRead (queue, &ioStatus, request->Data, request->Offset, request->Length); - - if (NT_SUCCESS (request->Item->Status) && ioStatus.Information != request->Length) - request->Item->Status = STATUS_END_OF_FILE; - } - } - - if (request->Item->Write) - { - queue->ReadAheadBufferValid = FALSE; - - ReleaseFragmentBuffer (queue, request->Data); - - if (request->CompleteOriginalIrp) - { - CompleteOriginalIrp (request->Item, request->Item->Status, - NT_SUCCESS (request->Item->Status) ? request->Item->OriginalLength : 0); - } - - ReleasePoolBuffer (queue, request); - } - else - { - BOOL readAhead = FALSE; - - if (NT_SUCCESS (request->Item->Status)) - memcpy (request->OrigDataBufferFragment, request->Data, request->Length); - - ReleaseFragmentBuffer (queue, request->Data); - request->Data = request->OrigDataBufferFragment; - - if (request->CompleteOriginalIrp - && queue->LastReadLength > 0 - && NT_SUCCESS (request->Item->Status) - && InterlockedExchangeAdd (&queue->IoThreadPendingRequestCount, 0) == 0) - { - readAhead = TRUE; - InterlockedIncrement (&queue->OutstandingIoCount); - } - - ExInterlockedInsertTailList (&queue->CompletionThreadQueue, &request->CompletionListEntry, &queue->CompletionThreadQueueLock); - KeSetEvent (&queue->CompletionThreadQueueNotEmptyEvent, IO_DISK_INCREMENT, FALSE); - - if (readAhead) - { - queue->ReadAheadBufferValid = FALSE; - queue->ReadAheadOffset.QuadPart = queue->LastReadOffset.QuadPart + queue->LastReadLength; - queue->ReadAheadLength = queue->LastReadLength; - - if (queue->ReadAheadOffset.QuadPart + queue->ReadAheadLength <= queue->MaxReadAheadOffset.QuadPart) - { -#ifdef TC_TRACE_IO_QUEUE - Dump ("A %I64d [%I64d] roff=%I64d rlen=%d\n", request->Item->OriginalIrpOffset.QuadPart, GetElapsedTime (&queue->LastPerformanceCounter), queue->ReadAheadOffset, queue->ReadAheadLength); -#endif - if (queue->IsFilterDevice) - { - queue->ReadAheadBufferValid = NT_SUCCESS (TCReadDevice (queue->LowerDeviceObject, queue->ReadAheadBuffer, queue->ReadAheadOffset, queue->ReadAheadLength)); - } - else - { - IO_STATUS_BLOCK ioStatus; - queue->ReadAheadBufferValid = NT_SUCCESS (ZwReadFile (queue->HostFileHandle, NULL, NULL, NULL, &ioStatus, queue->ReadAheadBuffer, queue->ReadAheadLength, &queue->ReadAheadOffset, NULL)); - queue->ReadAheadLength = (ULONG) ioStatus.Information; - } - } - - DecrementOutstandingIoCount (queue); - } - } - } - } - - PsTerminateSystemThread (STATUS_SUCCESS); -} - - -static VOID MainThreadProc (PVOID threadArg) -{ - EncryptedIoQueue *queue = (EncryptedIoQueue *) threadArg; - PLIST_ENTRY listEntry; - EncryptedIoQueueItem *item; - - LARGE_INTEGER fragmentOffset; - ULONG dataRemaining; - PUCHAR activeFragmentBuffer = queue->FragmentBufferA; - PUCHAR dataBuffer; - EncryptedIoRequest *request; - uint64 intersectStart; - uint32 intersectLength; - ULONGLONG addResult; - HRESULT hResult; - - if (IsEncryptionThreadPoolRunning()) - KeSetPriorityThread (KeGetCurrentThread(), LOW_REALTIME_PRIORITY); - - while (!queue->ThreadExitRequested) - { - if (!NT_SUCCESS (KeWaitForSingleObject (&queue->MainThreadQueueNotEmptyEvent, Executive, KernelMode, FALSE, NULL))) - continue; - - while ((listEntry = ExInterlockedRemoveHeadList (&queue->MainThreadQueue, &queue->MainThreadQueueLock))) - { - PIRP irp = CONTAINING_RECORD (listEntry, IRP, Tail.Overlay.ListEntry); - PIO_STACK_LOCATION irpSp = IoGetCurrentIrpStackLocation (irp); - - if (queue->Suspended) - KeWaitForSingleObject (&queue->QueueResumedEvent, Executive, KernelMode, FALSE, NULL); - - item = GetPoolBuffer (queue, sizeof (EncryptedIoQueueItem)); - if (!item) - { - TCCompleteDiskIrp (irp, STATUS_INSUFFICIENT_RESOURCES, 0); - DecrementOutstandingIoCount (queue); - IoReleaseRemoveLock (&queue->RemoveLock, irp); - - continue; - } - - item->Queue = queue; - item->OriginalIrp = irp; - item->Status = STATUS_SUCCESS; - - IoSetCancelRoutine (irp, NULL); - if (irp->Cancel) - { - CompleteOriginalIrp (item, STATUS_CANCELLED, 0); - continue; - } - - switch (irpSp->MajorFunction) - { - case IRP_MJ_READ: - item->Write = FALSE; - item->OriginalOffset = irpSp->Parameters.Read.ByteOffset; - item->OriginalLength = irpSp->Parameters.Read.Length; - break; - - case IRP_MJ_WRITE: - item->Write = TRUE; - item->OriginalOffset = irpSp->Parameters.Write.ByteOffset; - item->OriginalLength = irpSp->Parameters.Write.Length; - break; - - default: - CompleteOriginalIrp (item, STATUS_INVALID_PARAMETER, 0); - continue; - } - -#ifdef TC_TRACE_IO_QUEUE - item->OriginalIrpOffset = item->OriginalOffset; -#endif - - // Handle misaligned read operations to work around a bug in Windows System Assessment Tool which does not follow FILE_FLAG_NO_BUFFERING requirements when benchmarking disk devices - if (queue->IsFilterDevice - && !item->Write - && item->OriginalLength > 0 - && (item->OriginalLength & (ENCRYPTION_DATA_UNIT_SIZE - 1)) == 0 - && (item->OriginalOffset.QuadPart & (ENCRYPTION_DATA_UNIT_SIZE - 1)) != 0) - { - byte *buffer; - ULONG alignedLength; - LARGE_INTEGER alignedOffset; - hResult = ULongAdd(item->OriginalLength, ENCRYPTION_DATA_UNIT_SIZE, &alignedLength); - if (hResult != S_OK) - { - CompleteOriginalIrp (item, STATUS_INVALID_PARAMETER, 0); - continue; - } - - alignedOffset.QuadPart = item->OriginalOffset.QuadPart & ~((LONGLONG) ENCRYPTION_DATA_UNIT_SIZE - 1); - - buffer = TCalloc (alignedLength); - if (!buffer) - { - CompleteOriginalIrp (item, STATUS_INSUFFICIENT_RESOURCES, 0); - continue; - } - - item->Status = TCReadDevice (queue->LowerDeviceObject, buffer, alignedOffset, alignedLength); - - if (NT_SUCCESS (item->Status)) - { - UINT64_STRUCT dataUnit; - - dataBuffer = (PUCHAR) MmGetSystemAddressForMdlSafe (irp->MdlAddress, HighPagePriority); - if (!dataBuffer) - { - TCfree (buffer); - CompleteOriginalIrp (item, STATUS_INSUFFICIENT_RESOURCES, 0); - continue; - } - - if (queue->EncryptedAreaStart != -1 && queue->EncryptedAreaEnd != -1) - { - GetIntersection (alignedOffset.QuadPart, alignedLength, queue->EncryptedAreaStart, queue->EncryptedAreaEnd, &intersectStart, &intersectLength); - if (intersectLength > 0) - { - dataUnit.Value = intersectStart / ENCRYPTION_DATA_UNIT_SIZE; - DecryptDataUnits (buffer + (intersectStart - alignedOffset.QuadPart), &dataUnit, intersectLength / ENCRYPTION_DATA_UNIT_SIZE, queue->CryptoInfo); - } - } - - memcpy (dataBuffer, buffer + (item->OriginalOffset.LowPart & (ENCRYPTION_DATA_UNIT_SIZE - 1)), item->OriginalLength); - } - - TCfree (buffer); - CompleteOriginalIrp (item, item->Status, NT_SUCCESS (item->Status) ? item->OriginalLength : 0); - continue; - } - - // Validate offset and length - if (item->OriginalLength == 0 - || (item->OriginalLength & (ENCRYPTION_DATA_UNIT_SIZE - 1)) != 0 - || (item->OriginalOffset.QuadPart & (ENCRYPTION_DATA_UNIT_SIZE - 1)) != 0 - || ( !queue->IsFilterDevice && - ( (S_OK != ULongLongAdd(item->OriginalOffset.QuadPart, item->OriginalLength, &addResult)) - || (addResult > (ULONGLONG) queue->VirtualDeviceLength) - ) - ) - ) - { - CompleteOriginalIrp (item, STATUS_INVALID_PARAMETER, 0); - continue; - } - -#ifdef TC_TRACE_IO_QUEUE - Dump ("Q %I64d [%I64d] %c len=%d\n", item->OriginalOffset.QuadPart, GetElapsedTime (&queue->LastPerformanceCounter), item->Write ? 'W' : 'R', item->OriginalLength); -#endif - - if (!queue->IsFilterDevice) - { - // Adjust the offset for host file or device - if (queue->CryptoInfo->hiddenVolume) - hResult = ULongLongAdd(item->OriginalOffset.QuadPart, queue->CryptoInfo->hiddenVolumeOffset, &addResult); - else - hResult = ULongLongAdd(item->OriginalOffset.QuadPart, queue->CryptoInfo->volDataAreaOffset, &addResult); - - if (hResult != S_OK) - { - CompleteOriginalIrp (item, STATUS_INVALID_PARAMETER, 0); - continue; - } - else - item->OriginalOffset.QuadPart = addResult; - - // Hidden volume protection - if (item->Write && queue->CryptoInfo->bProtectHiddenVolume) - { - // If there has already been a write operation denied in order to protect the - // hidden volume (since the volume mount time) - if (queue->CryptoInfo->bHiddenVolProtectionAction) - { - // Do not allow writing to this volume anymore. This is to fake a complete volume - // or system failure (otherwise certain kinds of inconsistency within the file - // system could indicate that this volume has used hidden volume protection). - CompleteOriginalIrp (item, STATUS_INVALID_PARAMETER, 0); - continue; - } - - // Verify that no byte is going to be written to the hidden volume area - if (RegionsOverlap ((unsigned __int64) item->OriginalOffset.QuadPart, - (unsigned __int64) item->OriginalOffset.QuadPart + item->OriginalLength - 1, - queue->CryptoInfo->hiddenVolumeOffset, - (unsigned __int64) queue->CryptoInfo->hiddenVolumeOffset + queue->CryptoInfo->hiddenVolumeProtectedSize - 1)) - { - Dump ("Hidden volume protection triggered: write %I64d-%I64d (protected %I64d-%I64d)\n", item->OriginalOffset.QuadPart, item->OriginalOffset.QuadPart + item->OriginalLength - 1, queue->CryptoInfo->hiddenVolumeOffset, queue->CryptoInfo->hiddenVolumeOffset + queue->CryptoInfo->hiddenVolumeProtectedSize - 1); - queue->CryptoInfo->bHiddenVolProtectionAction = TRUE; - - // Deny this write operation to prevent the hidden volume from being overwritten - CompleteOriginalIrp (item, STATUS_INVALID_PARAMETER, 0); - continue; - } - } - } - else if (item->Write - && RegionsOverlap (item->OriginalOffset.QuadPart, item->OriginalOffset.QuadPart + item->OriginalLength - 1, TC_BOOT_VOLUME_HEADER_SECTOR_OFFSET, TC_BOOT_VOLUME_HEADER_SECTOR_OFFSET + TC_BOOT_ENCRYPTION_VOLUME_HEADER_SIZE - 1)) - { - // Prevent inappropriately designed software from damaging important data that may be out of sync with the backup on the Rescue Disk (such as the end of the encrypted area). - Dump ("Preventing write to the system encryption key data area\n"); - CompleteOriginalIrp (item, STATUS_MEDIA_WRITE_PROTECTED, 0); - continue; - } - else if (item->Write && IsHiddenSystemRunning() - && (RegionsOverlap (item->OriginalOffset.QuadPart, item->OriginalOffset.QuadPart + item->OriginalLength - 1, TC_SECTOR_SIZE_BIOS, TC_BOOT_LOADER_AREA_SECTOR_COUNT * TC_SECTOR_SIZE_BIOS - 1) - || RegionsOverlap (item->OriginalOffset.QuadPart, item->OriginalOffset.QuadPart + item->OriginalLength - 1, GetBootDriveLength(), _I64_MAX))) - { - Dump ("Preventing write to boot loader or host protected area\n"); - CompleteOriginalIrp (item, STATUS_MEDIA_WRITE_PROTECTED, 0); - continue; - } - - dataBuffer = (PUCHAR) MmGetSystemAddressForMdlSafe (irp->MdlAddress, HighPagePriority); - - if (dataBuffer == NULL) - { - CompleteOriginalIrp (item, STATUS_INSUFFICIENT_RESOURCES, 0); - continue; - } - - // Divide data block to fragments to enable efficient overlapping of encryption and IO operations - - dataRemaining = item->OriginalLength; - fragmentOffset = item->OriginalOffset; - - while (dataRemaining > 0) - { - BOOL isLastFragment = dataRemaining <= TC_ENC_IO_QUEUE_MAX_FRAGMENT_SIZE; - - ULONG dataFragmentLength = isLastFragment ? dataRemaining : TC_ENC_IO_QUEUE_MAX_FRAGMENT_SIZE; - activeFragmentBuffer = (activeFragmentBuffer == queue->FragmentBufferA ? queue->FragmentBufferB : queue->FragmentBufferA); - - InterlockedIncrement (&queue->IoThreadPendingRequestCount); - - // Create IO request - request = GetPoolBuffer (queue, sizeof (EncryptedIoRequest)); - if (!request) - { - CompleteOriginalIrp (item, STATUS_INSUFFICIENT_RESOURCES, 0); - break; - } - request->Item = item; - request->CompleteOriginalIrp = isLastFragment; - request->Offset = fragmentOffset; - request->Data = activeFragmentBuffer; - request->OrigDataBufferFragment = dataBuffer; - request->Length = dataFragmentLength; - - if (queue->IsFilterDevice) - { - if (queue->EncryptedAreaStart == -1 || queue->EncryptedAreaEnd == -1) - { - request->EncryptedLength = 0; - } - else - { - // Get intersection of data fragment with encrypted area - GetIntersection (fragmentOffset.QuadPart, dataFragmentLength, queue->EncryptedAreaStart, queue->EncryptedAreaEnd, &intersectStart, &intersectLength); - - request->EncryptedOffset = intersectStart - fragmentOffset.QuadPart; - request->EncryptedLength = intersectLength; - } - } - else - { - request->EncryptedOffset = 0; - request->EncryptedLength = dataFragmentLength; - } - - AcquireFragmentBuffer (queue, activeFragmentBuffer); - - if (item->Write) - { - // Encrypt data - memcpy (activeFragmentBuffer, dataBuffer, dataFragmentLength); - - if (request->EncryptedLength > 0) - { - UINT64_STRUCT dataUnit; - ASSERT (request->EncryptedOffset + request->EncryptedLength <= request->Offset.QuadPart + request->Length); - - dataUnit.Value = (request->Offset.QuadPart + request->EncryptedOffset) / ENCRYPTION_DATA_UNIT_SIZE; - - if (queue->CryptoInfo->bPartitionInInactiveSysEncScope) - dataUnit.Value += queue->CryptoInfo->FirstDataUnitNo.Value; - else if (queue->RemapEncryptedArea) - dataUnit.Value += queue->RemappedAreaDataUnitOffset; - - EncryptDataUnits (activeFragmentBuffer + request->EncryptedOffset, &dataUnit, request->EncryptedLength / ENCRYPTION_DATA_UNIT_SIZE, queue->CryptoInfo); - } - } - - // Queue IO request - ExInterlockedInsertTailList (&queue->IoThreadQueue, &request->ListEntry, &queue->IoThreadQueueLock); - KeSetEvent (&queue->IoThreadQueueNotEmptyEvent, IO_DISK_INCREMENT, FALSE); - - if (isLastFragment) - break; - - dataRemaining -= TC_ENC_IO_QUEUE_MAX_FRAGMENT_SIZE; - dataBuffer += TC_ENC_IO_QUEUE_MAX_FRAGMENT_SIZE; - fragmentOffset.QuadPart += TC_ENC_IO_QUEUE_MAX_FRAGMENT_SIZE; - } - } - } - - PsTerminateSystemThread (STATUS_SUCCESS); -} - - -NTSTATUS EncryptedIoQueueAddIrp (EncryptedIoQueue *queue, PIRP irp) -{ - NTSTATUS status; - - InterlockedIncrement (&queue->OutstandingIoCount); - if (queue->StopPending) - { - Dump ("STATUS_DEVICE_NOT_READY out=%d\n", queue->OutstandingIoCount); - status = STATUS_DEVICE_NOT_READY; - goto err; - } - - status = IoAcquireRemoveLock (&queue->RemoveLock, irp); - if (!NT_SUCCESS (status)) - goto err; - -#ifdef TC_TRACE_IO_QUEUE - { - PIO_STACK_LOCATION irpSp = IoGetCurrentIrpStackLocation (irp); - Dump ("* %I64d [%I64d] %c len=%d out=%d\n", irpSp->MajorFunction == IRP_MJ_WRITE ? irpSp->Parameters.Write.ByteOffset : irpSp->Parameters.Read.ByteOffset, GetElapsedTime (&queue->LastPerformanceCounter), irpSp->MajorFunction == IRP_MJ_WRITE ? 'W' : 'R', irpSp->MajorFunction == IRP_MJ_WRITE ? irpSp->Parameters.Write.Length : irpSp->Parameters.Read.Length, queue->OutstandingIoCount); - } -#endif - - IoMarkIrpPending (irp); - - ExInterlockedInsertTailList (&queue->MainThreadQueue, &irp->Tail.Overlay.ListEntry, &queue->MainThreadQueueLock); - KeSetEvent (&queue->MainThreadQueueNotEmptyEvent, IO_DISK_INCREMENT, FALSE); - - return STATUS_PENDING; - -err: - DecrementOutstandingIoCount (queue); - return status; -} - - -NTSTATUS EncryptedIoQueueHoldWhenIdle (EncryptedIoQueue *queue, int64 timeout) -{ - NTSTATUS status; - ASSERT (!queue->Suspended); - - queue->SuspendPending = TRUE; - - while (TRUE) - { - while (InterlockedExchangeAdd (&queue->OutstandingIoCount, 0) > 0) - { - LARGE_INTEGER waitTimeout; - - waitTimeout.QuadPart = timeout * -10000; - status = KeWaitForSingleObject (&queue->NoOutstandingIoEvent, Executive, KernelMode, FALSE, timeout != 0 ? &waitTimeout : NULL); - - if (status == STATUS_TIMEOUT) - status = STATUS_UNSUCCESSFUL; - - if (!NT_SUCCESS (status)) - { - queue->SuspendPending = FALSE; - return status; - } - - TCSleep (1); - if (InterlockedExchangeAdd (&queue->OutstandingIoCount, 0) > 0) - { - queue->SuspendPending = FALSE; - return STATUS_UNSUCCESSFUL; - } - } - - KeClearEvent (&queue->QueueResumedEvent); - queue->Suspended = TRUE; - - if (InterlockedExchangeAdd (&queue->OutstandingIoCount, 0) == 0) - break; - - queue->Suspended = FALSE; - KeSetEvent (&queue->QueueResumedEvent, IO_DISK_INCREMENT, FALSE); - } - - queue->ReadAheadBufferValid = FALSE; - - queue->SuspendPending = FALSE; - return STATUS_SUCCESS; -} - - -BOOL EncryptedIoQueueIsSuspended (EncryptedIoQueue *queue) -{ - return queue->Suspended; -} - - -BOOL EncryptedIoQueueIsRunning (EncryptedIoQueue *queue) -{ - return !queue->StopPending; -} - - -NTSTATUS EncryptedIoQueueResumeFromHold (EncryptedIoQueue *queue) -{ - ASSERT (queue->Suspended); - - queue->Suspended = FALSE; - KeSetEvent (&queue->QueueResumedEvent, IO_DISK_INCREMENT, FALSE); - - return STATUS_SUCCESS; -} - - -NTSTATUS EncryptedIoQueueStart (EncryptedIoQueue *queue) -{ - NTSTATUS status; - EncryptedIoQueueBuffer *buffer; - int i; - - queue->StartPending = TRUE; - queue->ThreadExitRequested = FALSE; - - queue->OutstandingIoCount = 0; - queue->IoThreadPendingRequestCount = 0; - - queue->FirstPoolBuffer = NULL; - KeInitializeMutex (&queue->BufferPoolMutex, 0); - - KeInitializeEvent (&queue->NoOutstandingIoEvent, SynchronizationEvent, FALSE); - KeInitializeEvent (&queue->PoolBufferFreeEvent, SynchronizationEvent, FALSE); - KeInitializeEvent (&queue->QueueResumedEvent, SynchronizationEvent, FALSE); - - queue->FragmentBufferA = TCalloc (TC_ENC_IO_QUEUE_MAX_FRAGMENT_SIZE); - if (!queue->FragmentBufferA) - goto noMemory; - - queue->FragmentBufferB = TCalloc (TC_ENC_IO_QUEUE_MAX_FRAGMENT_SIZE); - if (!queue->FragmentBufferB) - goto noMemory; - - KeInitializeEvent (&queue->FragmentBufferAFreeEvent, SynchronizationEvent, TRUE); - KeInitializeEvent (&queue->FragmentBufferBFreeEvent, SynchronizationEvent, TRUE); - - queue->ReadAheadBufferValid = FALSE; - queue->ReadAheadBuffer = TCalloc (TC_ENC_IO_QUEUE_MAX_FRAGMENT_SIZE); - if (!queue->ReadAheadBuffer) - goto noMemory; - - // Preallocate buffers - for (i = 0; i < TC_ENC_IO_QUEUE_PREALLOCATED_IO_REQUEST_COUNT; ++i) - { - if (i < TC_ENC_IO_QUEUE_PREALLOCATED_ITEM_COUNT && !GetPoolBuffer (queue, sizeof (EncryptedIoQueueItem))) - goto noMemory; - - if (!GetPoolBuffer (queue, sizeof (EncryptedIoRequest))) - goto noMemory; - } - - for (buffer = queue->FirstPoolBuffer; buffer != NULL; buffer = buffer->NextBuffer) - { - buffer->InUse = FALSE; - } - - // Main thread - InitializeListHead (&queue->MainThreadQueue); - KeInitializeSpinLock (&queue->MainThreadQueueLock); - KeInitializeEvent (&queue->MainThreadQueueNotEmptyEvent, SynchronizationEvent, FALSE); - - status = TCStartThread (MainThreadProc, queue, &queue->MainThread); - if (!NT_SUCCESS (status)) - goto err; - - // IO thread - InitializeListHead (&queue->IoThreadQueue); - KeInitializeSpinLock (&queue->IoThreadQueueLock); - KeInitializeEvent (&queue->IoThreadQueueNotEmptyEvent, SynchronizationEvent, FALSE); - - status = TCStartThread (IoThreadProc, queue, &queue->IoThread); - if (!NT_SUCCESS (status)) - { - queue->ThreadExitRequested = TRUE; - TCStopThread (queue->MainThread, &queue->MainThreadQueueNotEmptyEvent); - goto err; - } - - // Completion thread - InitializeListHead (&queue->CompletionThreadQueue); - KeInitializeSpinLock (&queue->CompletionThreadQueueLock); - KeInitializeEvent (&queue->CompletionThreadQueueNotEmptyEvent, SynchronizationEvent, FALSE); - - status = TCStartThread (CompletionThreadProc, queue, &queue->CompletionThread); - if (!NT_SUCCESS (status)) - { - queue->ThreadExitRequested = TRUE; - TCStopThread (queue->MainThread, &queue->MainThreadQueueNotEmptyEvent); - TCStopThread (queue->IoThread, &queue->IoThreadQueueNotEmptyEvent); - goto err; - } - -#ifdef TC_TRACE_IO_QUEUE - GetElapsedTimeInit (&queue->LastPerformanceCounter); -#endif - - queue->StopPending = FALSE; - queue->StartPending = FALSE; - - Dump ("Queue started\n"); - return STATUS_SUCCESS; - -noMemory: - status = STATUS_INSUFFICIENT_RESOURCES; - -err: - if (queue->FragmentBufferA) - TCfree (queue->FragmentBufferA); - if (queue->FragmentBufferB) - TCfree (queue->FragmentBufferB); - if (queue->ReadAheadBuffer) - TCfree (queue->ReadAheadBuffer); - - FreePoolBuffers (queue); - - queue->StartPending = FALSE; - return status; -} - - -NTSTATUS EncryptedIoQueueStop (EncryptedIoQueue *queue) -{ - ASSERT (!queue->StopPending); - queue->StopPending = TRUE; - - while (InterlockedExchangeAdd (&queue->OutstandingIoCount, 0) > 0) - { - KeWaitForSingleObject (&queue->NoOutstandingIoEvent, Executive, KernelMode, FALSE, NULL); - } - - Dump ("Queue stopping out=%d\n", queue->OutstandingIoCount); - - queue->ThreadExitRequested = TRUE; - - TCStopThread (queue->MainThread, &queue->MainThreadQueueNotEmptyEvent); - TCStopThread (queue->IoThread, &queue->IoThreadQueueNotEmptyEvent); - TCStopThread (queue->CompletionThread, &queue->CompletionThreadQueueNotEmptyEvent); - - TCfree (queue->FragmentBufferA); - TCfree (queue->FragmentBufferB); - TCfree (queue->ReadAheadBuffer); - - FreePoolBuffers (queue); - - Dump ("Queue stopped out=%d\n", queue->OutstandingIoCount); - return STATUS_SUCCESS; -} +/* + Derived from source code of TrueCrypt 7.1a, which is + Copyright (c) 2008-2012 TrueCrypt Developers Association and which is governed + by the TrueCrypt License 3.0. + + Modifications and additions to the original source code (contained in this file) + and all other portions of this file are Copyright (c) 2013-2016 IDRIX + and are governed by the Apache License 2.0 the full text of which is + contained in the file License.txt included in VeraCrypt binary and source + code distribution packages. +*/ + +#include "TCdefs.h" +#include "Apidrvr.h" +#include "Ntdriver.h" +#include "DriveFilter.h" +#include "EncryptedIoQueue.h" +#include "EncryptionThreadPool.h" +#include "Volumes.h" +#include + + +static void AcquireBufferPoolMutex (EncryptedIoQueue *queue) +{ + NTSTATUS status; + + status = KeWaitForMutexObject (&queue->BufferPoolMutex, Executive, KernelMode, FALSE, NULL); + if (!NT_SUCCESS (status)) + TC_BUG_CHECK (status); +} + + +static void ReleaseBufferPoolMutex (EncryptedIoQueue *queue) +{ + KeReleaseMutex (&queue->BufferPoolMutex, FALSE); +} + + +static void *GetPoolBuffer (EncryptedIoQueue *queue, ULONG requestedSize) +{ + EncryptedIoQueueBuffer *buffer; + void *bufferAddress = NULL; + BOOL requestedSizePresentInPool = FALSE; + + while (TRUE) + { + AcquireBufferPoolMutex (queue); + + for (buffer = queue->FirstPoolBuffer; ; buffer = buffer->NextBuffer) + { + if (buffer && buffer->Size == requestedSize) + { + requestedSizePresentInPool = TRUE; + + if (!buffer->InUse) + { + // Reuse a free buffer + buffer->InUse = TRUE; + bufferAddress = buffer->Address; + break; + } + } + + if (!buffer || !buffer->NextBuffer) + { + EncryptedIoQueueBuffer *newBuffer; + + if (requestedSizePresentInPool && !queue->StartPending) + break; + + // Allocate a new buffer + newBuffer = TCalloc (sizeof (EncryptedIoQueueBuffer)); + if (!newBuffer) + { + bufferAddress = NULL; + break; + } + + bufferAddress = TCalloc (requestedSize); + if (bufferAddress) + { + newBuffer->NextBuffer = NULL; + newBuffer->Address = bufferAddress; + newBuffer->Size = requestedSize; + newBuffer->InUse = TRUE; + + if (!buffer) + queue->FirstPoolBuffer = newBuffer; + else + buffer->NextBuffer = newBuffer; + } + else + TCfree (newBuffer); + + break; + } + } + + ReleaseBufferPoolMutex (queue); + + if (bufferAddress || !requestedSizePresentInPool || queue->StartPending) + break; + + KeWaitForSingleObject (&queue->PoolBufferFreeEvent, Executive, KernelMode, FALSE, NULL); + } + + return bufferAddress; +} + + +static void ReleasePoolBuffer (EncryptedIoQueue *queue, void *address) +{ + EncryptedIoQueueBuffer *buffer; + AcquireBufferPoolMutex (queue); + + for (buffer = queue->FirstPoolBuffer; buffer != NULL; buffer = buffer->NextBuffer) + { + if (buffer->Address == address) + { + ASSERT (buffer->InUse); + + buffer->InUse = FALSE; + break; + } + } + + ReleaseBufferPoolMutex (queue); + KeSetEvent (&queue->PoolBufferFreeEvent, IO_DISK_INCREMENT, FALSE); +} + + +static void FreePoolBuffers (EncryptedIoQueue *queue) +{ + EncryptedIoQueueBuffer *buffer; + AcquireBufferPoolMutex (queue); + + for (buffer = queue->FirstPoolBuffer; buffer != NULL; ) + { + EncryptedIoQueueBuffer *nextBuffer = buffer->NextBuffer; + + ASSERT (!buffer->InUse || queue->StartPending); + + TCfree (buffer->Address); + TCfree (buffer); + + buffer = nextBuffer; + } + + queue->FirstPoolBuffer = NULL; + ReleaseBufferPoolMutex (queue); +} + + +static void DecrementOutstandingIoCount (EncryptedIoQueue *queue) +{ + if (InterlockedDecrement (&queue->OutstandingIoCount) == 0 && (queue->SuspendPending || queue->StopPending)) + KeSetEvent (&queue->NoOutstandingIoEvent, IO_DISK_INCREMENT, FALSE); +} + + +static void OnItemCompleted (EncryptedIoQueueItem *item, BOOL freeItem) +{ + DecrementOutstandingIoCount (item->Queue); + IoReleaseRemoveLock (&item->Queue->RemoveLock, item->OriginalIrp); + + if (NT_SUCCESS (item->Status)) + { + if (item->Write) + item->Queue->TotalBytesWritten += item->OriginalLength; + else + item->Queue->TotalBytesRead += item->OriginalLength; + } + + if (freeItem) + ReleasePoolBuffer (item->Queue, item); +} + + +static NTSTATUS CompleteOriginalIrp (EncryptedIoQueueItem *item, NTSTATUS status, ULONG_PTR information) +{ +#ifdef TC_TRACE_IO_QUEUE + Dump ("< %I64d [%I64d] %c status=%x info=%I64d\n", item->OriginalIrpOffset, GetElapsedTime (&item->Queue->LastPerformanceCounter), item->Write ? 'W' : 'R', status, (int64) information); +#endif + + TCCompleteDiskIrp (item->OriginalIrp, status, information); + + item->Status = status; + OnItemCompleted (item, TRUE); + + return status; +} + + +static void AcquireFragmentBuffer (EncryptedIoQueue *queue, byte *buffer) +{ + NTSTATUS status = STATUS_INVALID_PARAMETER; + + if (buffer == queue->FragmentBufferA) + { + status = KeWaitForSingleObject (&queue->FragmentBufferAFreeEvent, Executive, KernelMode, FALSE, NULL); + } + else if (buffer == queue->FragmentBufferB) + { + status = KeWaitForSingleObject (&queue->FragmentBufferBFreeEvent, Executive, KernelMode, FALSE, NULL); + } + + if (!NT_SUCCESS (status)) + TC_BUG_CHECK (status); +} + + +static void ReleaseFragmentBuffer (EncryptedIoQueue *queue, byte *buffer) +{ + if (buffer == queue->FragmentBufferA) + { + KeSetEvent (&queue->FragmentBufferAFreeEvent, IO_DISK_INCREMENT, FALSE); + } + else if (buffer == queue->FragmentBufferB) + { + KeSetEvent (&queue->FragmentBufferBFreeEvent, IO_DISK_INCREMENT, FALSE); + } + else + { + TC_BUG_CHECK (STATUS_INVALID_PARAMETER); + } +} + + +static VOID CompletionThreadProc (PVOID threadArg) +{ + EncryptedIoQueue *queue = (EncryptedIoQueue *) threadArg; + PLIST_ENTRY listEntry; + EncryptedIoRequest *request; + UINT64_STRUCT dataUnit; + + if (IsEncryptionThreadPoolRunning()) + KeSetPriorityThread (KeGetCurrentThread(), LOW_REALTIME_PRIORITY); + + while (!queue->ThreadExitRequested) + { + if (!NT_SUCCESS (KeWaitForSingleObject (&queue->CompletionThreadQueueNotEmptyEvent, Executive, KernelMode, FALSE, NULL))) + continue; + + if (queue->ThreadExitRequested) + break; + + while ((listEntry = ExInterlockedRemoveHeadList (&queue->CompletionThreadQueue, &queue->CompletionThreadQueueLock))) + { + request = CONTAINING_RECORD (listEntry, EncryptedIoRequest, CompletionListEntry); + + if (request->EncryptedLength > 0 && NT_SUCCESS (request->Item->Status)) + { + ASSERT (request->EncryptedOffset + request->EncryptedLength <= request->Offset.QuadPart + request->Length); + dataUnit.Value = (request->Offset.QuadPart + request->EncryptedOffset) / ENCRYPTION_DATA_UNIT_SIZE; + + if (queue->CryptoInfo->bPartitionInInactiveSysEncScope) + dataUnit.Value += queue->CryptoInfo->FirstDataUnitNo.Value; + else if (queue->RemapEncryptedArea) + dataUnit.Value += queue->RemappedAreaDataUnitOffset; + + DecryptDataUnits (request->Data + request->EncryptedOffset, &dataUnit, request->EncryptedLength / ENCRYPTION_DATA_UNIT_SIZE, queue->CryptoInfo); + } + + if (request->CompleteOriginalIrp) + { + CompleteOriginalIrp (request->Item, request->Item->Status, + NT_SUCCESS (request->Item->Status) ? request->Item->OriginalLength : 0); + } + + ReleasePoolBuffer (queue, request); + } + } + + PsTerminateSystemThread (STATUS_SUCCESS); +} + + +static NTSTATUS TCCachedRead (EncryptedIoQueue *queue, IO_STATUS_BLOCK *ioStatus, PVOID buffer, LARGE_INTEGER offset, ULONG length) +{ + queue->LastReadOffset = offset; + queue->LastReadLength = length; + + if (queue->ReadAheadBufferValid && queue->ReadAheadOffset.QuadPart == offset.QuadPart && queue->ReadAheadLength >= length) + { + memcpy (buffer, queue->ReadAheadBuffer, length); + + if (!queue->IsFilterDevice) + { + ioStatus->Information = length; + ioStatus->Status = STATUS_SUCCESS; + } + + return STATUS_SUCCESS; + } + + if (queue->IsFilterDevice) + return TCReadDevice (queue->LowerDeviceObject, buffer, offset, length); + + return ZwReadFile (queue->HostFileHandle, NULL, NULL, NULL, ioStatus, buffer, length, &offset, NULL); +} + + +static VOID IoThreadProc (PVOID threadArg) +{ + EncryptedIoQueue *queue = (EncryptedIoQueue *) threadArg; + PLIST_ENTRY listEntry; + EncryptedIoRequest *request; + + KeSetPriorityThread (KeGetCurrentThread(), LOW_REALTIME_PRIORITY); + + if (!queue->IsFilterDevice && queue->SecurityClientContext) + { +#ifdef DEBUG + NTSTATUS status = +#endif + SeImpersonateClientEx (queue->SecurityClientContext, NULL); + ASSERT (NT_SUCCESS (status)); + } + + while (!queue->ThreadExitRequested) + { + if (!NT_SUCCESS (KeWaitForSingleObject (&queue->IoThreadQueueNotEmptyEvent, Executive, KernelMode, FALSE, NULL))) + continue; + + if (queue->ThreadExitRequested) + break; + + while ((listEntry = ExInterlockedRemoveHeadList (&queue->IoThreadQueue, &queue->IoThreadQueueLock))) + { + InterlockedDecrement (&queue->IoThreadPendingRequestCount); + request = CONTAINING_RECORD (listEntry, EncryptedIoRequest, ListEntry); + +#ifdef TC_TRACE_IO_QUEUE + Dump ("%c %I64d [%I64d] roff=%I64d rlen=%d\n", request->Item->Write ? 'W' : 'R', request->Item->OriginalIrpOffset.QuadPart, GetElapsedTime (&queue->LastPerformanceCounter), request->Offset.QuadPart, request->Length); +#endif + + // Perform IO request if no preceding request of the item failed + if (NT_SUCCESS (request->Item->Status)) + { + if (queue->IsFilterDevice) + { + if (queue->RemapEncryptedArea && request->EncryptedLength > 0) + { + if (request->EncryptedLength != request->Length) + { + // Up to three subfragments may be required to handle a partially remapped fragment + int subFragment; + byte *subFragmentData = request->Data; + + for (subFragment = 0 ; subFragment < 3; ++subFragment) + { + LARGE_INTEGER subFragmentOffset; + ULONG subFragmentLength; + subFragmentOffset.QuadPart = request->Offset.QuadPart; + + switch (subFragment) + { + case 0: + subFragmentLength = (ULONG) request->EncryptedOffset; + break; + + case 1: + subFragmentOffset.QuadPart += request->EncryptedOffset + queue->RemappedAreaOffset; + subFragmentLength = request->EncryptedLength; + break; + + case 2: + subFragmentOffset.QuadPart += request->EncryptedOffset + request->EncryptedLength; + subFragmentLength = (ULONG) (request->Length - (request->EncryptedOffset + request->EncryptedLength)); + break; + } + + if (subFragmentLength > 0) + { + if (request->Item->Write) + request->Item->Status = TCWriteDevice (queue->LowerDeviceObject, subFragmentData, subFragmentOffset, subFragmentLength); + else + request->Item->Status = TCCachedRead (queue, NULL, subFragmentData, subFragmentOffset, subFragmentLength); + + subFragmentData += subFragmentLength; + } + } + } + else + { + // Remap the fragment + LARGE_INTEGER remappedOffset; + remappedOffset.QuadPart = request->Offset.QuadPart + queue->RemappedAreaOffset; + + if (request->Item->Write) + request->Item->Status = TCWriteDevice (queue->LowerDeviceObject, request->Data, remappedOffset, request->Length); + else + request->Item->Status = TCCachedRead (queue, NULL, request->Data, remappedOffset, request->Length); + } + } + else + { + if (request->Item->Write) + request->Item->Status = TCWriteDevice (queue->LowerDeviceObject, request->Data, request->Offset, request->Length); + else + request->Item->Status = TCCachedRead (queue, NULL, request->Data, request->Offset, request->Length); + } + } + else + { + IO_STATUS_BLOCK ioStatus; + + if (request->Item->Write) + request->Item->Status = ZwWriteFile (queue->HostFileHandle, NULL, NULL, NULL, &ioStatus, request->Data, request->Length, &request->Offset, NULL); + else + request->Item->Status = TCCachedRead (queue, &ioStatus, request->Data, request->Offset, request->Length); + + if (NT_SUCCESS (request->Item->Status) && ioStatus.Information != request->Length) + request->Item->Status = STATUS_END_OF_FILE; + } + } + + if (request->Item->Write) + { + queue->ReadAheadBufferValid = FALSE; + + ReleaseFragmentBuffer (queue, request->Data); + + if (request->CompleteOriginalIrp) + { + CompleteOriginalIrp (request->Item, request->Item->Status, + NT_SUCCESS (request->Item->Status) ? request->Item->OriginalLength : 0); + } + + ReleasePoolBuffer (queue, request); + } + else + { + BOOL readAhead = FALSE; + + if (NT_SUCCESS (request->Item->Status)) + memcpy (request->OrigDataBufferFragment, request->Data, request->Length); + + ReleaseFragmentBuffer (queue, request->Data); + request->Data = request->OrigDataBufferFragment; + + if (request->CompleteOriginalIrp + && queue->LastReadLength > 0 + && NT_SUCCESS (request->Item->Status) + && InterlockedExchangeAdd (&queue->IoThreadPendingRequestCount, 0) == 0) + { + readAhead = TRUE; + InterlockedIncrement (&queue->OutstandingIoCount); + } + + ExInterlockedInsertTailList (&queue->CompletionThreadQueue, &request->CompletionListEntry, &queue->CompletionThreadQueueLock); + KeSetEvent (&queue->CompletionThreadQueueNotEmptyEvent, IO_DISK_INCREMENT, FALSE); + + if (readAhead) + { + queue->ReadAheadBufferValid = FALSE; + queue->ReadAheadOffset.QuadPart = queue->LastReadOffset.QuadPart + queue->LastReadLength; + queue->ReadAheadLength = queue->LastReadLength; + + if (queue->ReadAheadOffset.QuadPart + queue->ReadAheadLength <= queue->MaxReadAheadOffset.QuadPart) + { +#ifdef TC_TRACE_IO_QUEUE + Dump ("A %I64d [%I64d] roff=%I64d rlen=%d\n", request->Item->OriginalIrpOffset.QuadPart, GetElapsedTime (&queue->LastPerformanceCounter), queue->ReadAheadOffset, queue->ReadAheadLength); +#endif + if (queue->IsFilterDevice) + { + queue->ReadAheadBufferValid = NT_SUCCESS (TCReadDevice (queue->LowerDeviceObject, queue->ReadAheadBuffer, queue->ReadAheadOffset, queue->ReadAheadLength)); + } + else + { + IO_STATUS_BLOCK ioStatus; + queue->ReadAheadBufferValid = NT_SUCCESS (ZwReadFile (queue->HostFileHandle, NULL, NULL, NULL, &ioStatus, queue->ReadAheadBuffer, queue->ReadAheadLength, &queue->ReadAheadOffset, NULL)); + queue->ReadAheadLength = (ULONG) ioStatus.Information; + } + } + + DecrementOutstandingIoCount (queue); + } + } + } + } + + PsTerminateSystemThread (STATUS_SUCCESS); +} + + +static VOID MainThreadProc (PVOID threadArg) +{ + EncryptedIoQueue *queue = (EncryptedIoQueue *) threadArg; + PLIST_ENTRY listEntry; + EncryptedIoQueueItem *item; + + LARGE_INTEGER fragmentOffset; + ULONG dataRemaining; + PUCHAR activeFragmentBuffer = queue->FragmentBufferA; + PUCHAR dataBuffer; + EncryptedIoRequest *request; + uint64 intersectStart; + uint32 intersectLength; + ULONGLONG addResult; + HRESULT hResult; + + if (IsEncryptionThreadPoolRunning()) + KeSetPriorityThread (KeGetCurrentThread(), LOW_REALTIME_PRIORITY); + + while (!queue->ThreadExitRequested) + { + if (!NT_SUCCESS (KeWaitForSingleObject (&queue->MainThreadQueueNotEmptyEvent, Executive, KernelMode, FALSE, NULL))) + continue; + + while ((listEntry = ExInterlockedRemoveHeadList (&queue->MainThreadQueue, &queue->MainThreadQueueLock))) + { + PIRP irp = CONTAINING_RECORD (listEntry, IRP, Tail.Overlay.ListEntry); + PIO_STACK_LOCATION irpSp = IoGetCurrentIrpStackLocation (irp); + + if (queue->Suspended) + KeWaitForSingleObject (&queue->QueueResumedEvent, Executive, KernelMode, FALSE, NULL); + + item = GetPoolBuffer (queue, sizeof (EncryptedIoQueueItem)); + if (!item) + { + TCCompleteDiskIrp (irp, STATUS_INSUFFICIENT_RESOURCES, 0); + DecrementOutstandingIoCount (queue); + IoReleaseRemoveLock (&queue->RemoveLock, irp); + + continue; + } + + item->Queue = queue; + item->OriginalIrp = irp; + item->Status = STATUS_SUCCESS; + + IoSetCancelRoutine (irp, NULL); + if (irp->Cancel) + { + CompleteOriginalIrp (item, STATUS_CANCELLED, 0); + continue; + } + + switch (irpSp->MajorFunction) + { + case IRP_MJ_READ: + item->Write = FALSE; + item->OriginalOffset = irpSp->Parameters.Read.ByteOffset; + item->OriginalLength = irpSp->Parameters.Read.Length; + break; + + case IRP_MJ_WRITE: + item->Write = TRUE; + item->OriginalOffset = irpSp->Parameters.Write.ByteOffset; + item->OriginalLength = irpSp->Parameters.Write.Length; + break; + + default: + CompleteOriginalIrp (item, STATUS_INVALID_PARAMETER, 0); + continue; + } + +#ifdef TC_TRACE_IO_QUEUE + item->OriginalIrpOffset = item->OriginalOffset; +#endif + + // Handle misaligned read operations to work around a bug in Windows System Assessment Tool which does not follow FILE_FLAG_NO_BUFFERING requirements when benchmarking disk devices + if (queue->IsFilterDevice + && !item->Write + && item->OriginalLength > 0 + && (item->OriginalLength & (ENCRYPTION_DATA_UNIT_SIZE - 1)) == 0 + && (item->OriginalOffset.QuadPart & (ENCRYPTION_DATA_UNIT_SIZE - 1)) != 0) + { + byte *buffer; + ULONG alignedLength; + LARGE_INTEGER alignedOffset; + hResult = ULongAdd(item->OriginalLength, ENCRYPTION_DATA_UNIT_SIZE, &alignedLength); + if (hResult != S_OK) + { + CompleteOriginalIrp (item, STATUS_INVALID_PARAMETER, 0); + continue; + } + + alignedOffset.QuadPart = item->OriginalOffset.QuadPart & ~((LONGLONG) ENCRYPTION_DATA_UNIT_SIZE - 1); + + buffer = TCalloc (alignedLength); + if (!buffer) + { + CompleteOriginalIrp (item, STATUS_INSUFFICIENT_RESOURCES, 0); + continue; + } + + item->Status = TCReadDevice (queue->LowerDeviceObject, buffer, alignedOffset, alignedLength); + + if (NT_SUCCESS (item->Status)) + { + UINT64_STRUCT dataUnit; + + dataBuffer = (PUCHAR) MmGetSystemAddressForMdlSafe (irp->MdlAddress, HighPagePriority); + if (!dataBuffer) + { + TCfree (buffer); + CompleteOriginalIrp (item, STATUS_INSUFFICIENT_RESOURCES, 0); + continue; + } + + if (queue->EncryptedAreaStart != -1 && queue->EncryptedAreaEnd != -1) + { + GetIntersection (alignedOffset.QuadPart, alignedLength, queue->EncryptedAreaStart, queue->EncryptedAreaEnd, &intersectStart, &intersectLength); + if (intersectLength > 0) + { + dataUnit.Value = intersectStart / ENCRYPTION_DATA_UNIT_SIZE; + DecryptDataUnits (buffer + (intersectStart - alignedOffset.QuadPart), &dataUnit, intersectLength / ENCRYPTION_DATA_UNIT_SIZE, queue->CryptoInfo); + } + } + + memcpy (dataBuffer, buffer + (item->OriginalOffset.LowPart & (ENCRYPTION_DATA_UNIT_SIZE - 1)), item->OriginalLength); + } + + TCfree (buffer); + CompleteOriginalIrp (item, item->Status, NT_SUCCESS (item->Status) ? item->OriginalLength : 0); + continue; + } + + // Validate offset and length + if (item->OriginalLength == 0 + || (item->OriginalLength & (ENCRYPTION_DATA_UNIT_SIZE - 1)) != 0 + || (item->OriginalOffset.QuadPart & (ENCRYPTION_DATA_UNIT_SIZE - 1)) != 0 + || ( !queue->IsFilterDevice && + ( (S_OK != ULongLongAdd(item->OriginalOffset.QuadPart, item->OriginalLength, &addResult)) + || (addResult > (ULONGLONG) queue->VirtualDeviceLength) + ) + ) + ) + { + CompleteOriginalIrp (item, STATUS_INVALID_PARAMETER, 0); + continue; + } + +#ifdef TC_TRACE_IO_QUEUE + Dump ("Q %I64d [%I64d] %c len=%d\n", item->OriginalOffset.QuadPart, GetElapsedTime (&queue->LastPerformanceCounter), item->Write ? 'W' : 'R', item->OriginalLength); +#endif + + if (!queue->IsFilterDevice) + { + // Adjust the offset for host file or device + if (queue->CryptoInfo->hiddenVolume) + hResult = ULongLongAdd(item->OriginalOffset.QuadPart, queue->CryptoInfo->hiddenVolumeOffset, &addResult); + else + hResult = ULongLongAdd(item->OriginalOffset.QuadPart, queue->CryptoInfo->volDataAreaOffset, &addResult); + + if (hResult != S_OK) + { + CompleteOriginalIrp (item, STATUS_INVALID_PARAMETER, 0); + continue; + } + else + item->OriginalOffset.QuadPart = addResult; + + // Hidden volume protection + if (item->Write && queue->CryptoInfo->bProtectHiddenVolume) + { + // If there has already been a write operation denied in order to protect the + // hidden volume (since the volume mount time) + if (queue->CryptoInfo->bHiddenVolProtectionAction) + { + // Do not allow writing to this volume anymore. This is to fake a complete volume + // or system failure (otherwise certain kinds of inconsistency within the file + // system could indicate that this volume has used hidden volume protection). + CompleteOriginalIrp (item, STATUS_INVALID_PARAMETER, 0); + continue; + } + + // Verify that no byte is going to be written to the hidden volume area + if (RegionsOverlap ((unsigned __int64) item->OriginalOffset.QuadPart, + (unsigned __int64) item->OriginalOffset.QuadPart + item->OriginalLength - 1, + queue->CryptoInfo->hiddenVolumeOffset, + (unsigned __int64) queue->CryptoInfo->hiddenVolumeOffset + queue->CryptoInfo->hiddenVolumeProtectedSize - 1)) + { + Dump ("Hidden volume protection triggered: write %I64d-%I64d (protected %I64d-%I64d)\n", item->OriginalOffset.QuadPart, item->OriginalOffset.QuadPart + item->OriginalLength - 1, queue->CryptoInfo->hiddenVolumeOffset, queue->CryptoInfo->hiddenVolumeOffset + queue->CryptoInfo->hiddenVolumeProtectedSize - 1); + queue->CryptoInfo->bHiddenVolProtectionAction = TRUE; + + // Deny this write operation to prevent the hidden volume from being overwritten + CompleteOriginalIrp (item, STATUS_INVALID_PARAMETER, 0); + continue; + } + } + } + else if (item->Write + && RegionsOverlap (item->OriginalOffset.QuadPart, item->OriginalOffset.QuadPart + item->OriginalLength - 1, TC_BOOT_VOLUME_HEADER_SECTOR_OFFSET, TC_BOOT_VOLUME_HEADER_SECTOR_OFFSET + TC_BOOT_ENCRYPTION_VOLUME_HEADER_SIZE - 1)) + { + // Prevent inappropriately designed software from damaging important data that may be out of sync with the backup on the Rescue Disk (such as the end of the encrypted area). + Dump ("Preventing write to the system encryption key data area\n"); + CompleteOriginalIrp (item, STATUS_MEDIA_WRITE_PROTECTED, 0); + continue; + } + else if (item->Write && IsHiddenSystemRunning() + && (RegionsOverlap (item->OriginalOffset.QuadPart, item->OriginalOffset.QuadPart + item->OriginalLength - 1, TC_SECTOR_SIZE_BIOS, TC_BOOT_LOADER_AREA_SECTOR_COUNT * TC_SECTOR_SIZE_BIOS - 1) + || RegionsOverlap (item->OriginalOffset.QuadPart, item->OriginalOffset.QuadPart + item->OriginalLength - 1, GetBootDriveLength(), _I64_MAX))) + { + Dump ("Preventing write to boot loader or host protected area\n"); + CompleteOriginalIrp (item, STATUS_MEDIA_WRITE_PROTECTED, 0); + continue; + } + + dataBuffer = (PUCHAR) MmGetSystemAddressForMdlSafe (irp->MdlAddress, HighPagePriority); + + if (dataBuffer == NULL) + { + CompleteOriginalIrp (item, STATUS_INSUFFICIENT_RESOURCES, 0); + continue; + } + + // Divide data block to fragments to enable efficient overlapping of encryption and IO operations + + dataRemaining = item->OriginalLength; + fragmentOffset = item->OriginalOffset; + + while (dataRemaining > 0) + { + BOOL isLastFragment = dataRemaining <= TC_ENC_IO_QUEUE_MAX_FRAGMENT_SIZE; + + ULONG dataFragmentLength = isLastFragment ? dataRemaining : TC_ENC_IO_QUEUE_MAX_FRAGMENT_SIZE; + activeFragmentBuffer = (activeFragmentBuffer == queue->FragmentBufferA ? queue->FragmentBufferB : queue->FragmentBufferA); + + InterlockedIncrement (&queue->IoThreadPendingRequestCount); + + // Create IO request + request = GetPoolBuffer (queue, sizeof (EncryptedIoRequest)); + if (!request) + { + CompleteOriginalIrp (item, STATUS_INSUFFICIENT_RESOURCES, 0); + break; + } + request->Item = item; + request->CompleteOriginalIrp = isLastFragment; + request->Offset = fragmentOffset; + request->Data = activeFragmentBuffer; + request->OrigDataBufferFragment = dataBuffer; + request->Length = dataFragmentLength; + + if (queue->IsFilterDevice) + { + if (queue->EncryptedAreaStart == -1 || queue->EncryptedAreaEnd == -1) + { + request->EncryptedLength = 0; + } + else + { + // Get intersection of data fragment with encrypted area + GetIntersection (fragmentOffset.QuadPart, dataFragmentLength, queue->EncryptedAreaStart, queue->EncryptedAreaEnd, &intersectStart, &intersectLength); + + request->EncryptedOffset = intersectStart - fragmentOffset.QuadPart; + request->EncryptedLength = intersectLength; + } + } + else + { + request->EncryptedOffset = 0; + request->EncryptedLength = dataFragmentLength; + } + + AcquireFragmentBuffer (queue, activeFragmentBuffer); + + if (item->Write) + { + // Encrypt data + memcpy (activeFragmentBuffer, dataBuffer, dataFragmentLength); + + if (request->EncryptedLength > 0) + { + UINT64_STRUCT dataUnit; + ASSERT (request->EncryptedOffset + request->EncryptedLength <= request->Offset.QuadPart + request->Length); + + dataUnit.Value = (request->Offset.QuadPart + request->EncryptedOffset) / ENCRYPTION_DATA_UNIT_SIZE; + + if (queue->CryptoInfo->bPartitionInInactiveSysEncScope) + dataUnit.Value += queue->CryptoInfo->FirstDataUnitNo.Value; + else if (queue->RemapEncryptedArea) + dataUnit.Value += queue->RemappedAreaDataUnitOffset; + + EncryptDataUnits (activeFragmentBuffer + request->EncryptedOffset, &dataUnit, request->EncryptedLength / ENCRYPTION_DATA_UNIT_SIZE, queue->CryptoInfo); + } + } + + // Queue IO request + ExInterlockedInsertTailList (&queue->IoThreadQueue, &request->ListEntry, &queue->IoThreadQueueLock); + KeSetEvent (&queue->IoThreadQueueNotEmptyEvent, IO_DISK_INCREMENT, FALSE); + + if (isLastFragment) + break; + + dataRemaining -= TC_ENC_IO_QUEUE_MAX_FRAGMENT_SIZE; + dataBuffer += TC_ENC_IO_QUEUE_MAX_FRAGMENT_SIZE; + fragmentOffset.QuadPart += TC_ENC_IO_QUEUE_MAX_FRAGMENT_SIZE; + } + } + } + + PsTerminateSystemThread (STATUS_SUCCESS); +} + + +NTSTATUS EncryptedIoQueueAddIrp (EncryptedIoQueue *queue, PIRP irp) +{ + NTSTATUS status; + + InterlockedIncrement (&queue->OutstandingIoCount); + if (queue->StopPending) + { + Dump ("STATUS_DEVICE_NOT_READY out=%d\n", queue->OutstandingIoCount); + status = STATUS_DEVICE_NOT_READY; + goto err; + } + + status = IoAcquireRemoveLock (&queue->RemoveLock, irp); + if (!NT_SUCCESS (status)) + goto err; + +#ifdef TC_TRACE_IO_QUEUE + { + PIO_STACK_LOCATION irpSp = IoGetCurrentIrpStackLocation (irp); + Dump ("* %I64d [%I64d] %c len=%d out=%d\n", irpSp->MajorFunction == IRP_MJ_WRITE ? irpSp->Parameters.Write.ByteOffset : irpSp->Parameters.Read.ByteOffset, GetElapsedTime (&queue->LastPerformanceCounter), irpSp->MajorFunction == IRP_MJ_WRITE ? 'W' : 'R', irpSp->MajorFunction == IRP_MJ_WRITE ? irpSp->Parameters.Write.Length : irpSp->Parameters.Read.Length, queue->OutstandingIoCount); + } +#endif + + IoMarkIrpPending (irp); + + ExInterlockedInsertTailList (&queue->MainThreadQueue, &irp->Tail.Overlay.ListEntry, &queue->MainThreadQueueLock); + KeSetEvent (&queue->MainThreadQueueNotEmptyEvent, IO_DISK_INCREMENT, FALSE); + + return STATUS_PENDING; + +err: + DecrementOutstandingIoCount (queue); + return status; +} + + +NTSTATUS EncryptedIoQueueHoldWhenIdle (EncryptedIoQueue *queue, int64 timeout) +{ + NTSTATUS status; + ASSERT (!queue->Suspended); + + queue->SuspendPending = TRUE; + + while (TRUE) + { + while (InterlockedExchangeAdd (&queue->OutstandingIoCount, 0) > 0) + { + LARGE_INTEGER waitTimeout; + + waitTimeout.QuadPart = timeout * -10000; + status = KeWaitForSingleObject (&queue->NoOutstandingIoEvent, Executive, KernelMode, FALSE, timeout != 0 ? &waitTimeout : NULL); + + if (status == STATUS_TIMEOUT) + status = STATUS_UNSUCCESSFUL; + + if (!NT_SUCCESS (status)) + { + queue->SuspendPending = FALSE; + return status; + } + + TCSleep (1); + if (InterlockedExchangeAdd (&queue->OutstandingIoCount, 0) > 0) + { + queue->SuspendPending = FALSE; + return STATUS_UNSUCCESSFUL; + } + } + + KeClearEvent (&queue->QueueResumedEvent); + queue->Suspended = TRUE; + + if (InterlockedExchangeAdd (&queue->OutstandingIoCount, 0) == 0) + break; + + queue->Suspended = FALSE; + KeSetEvent (&queue->QueueResumedEvent, IO_DISK_INCREMENT, FALSE); + } + + queue->ReadAheadBufferValid = FALSE; + + queue->SuspendPending = FALSE; + return STATUS_SUCCESS; +} + + +BOOL EncryptedIoQueueIsSuspended (EncryptedIoQueue *queue) +{ + return queue->Suspended; +} + + +BOOL EncryptedIoQueueIsRunning (EncryptedIoQueue *queue) +{ + return !queue->StopPending; +} + + +NTSTATUS EncryptedIoQueueResumeFromHold (EncryptedIoQueue *queue) +{ + ASSERT (queue->Suspended); + + queue->Suspended = FALSE; + KeSetEvent (&queue->QueueResumedEvent, IO_DISK_INCREMENT, FALSE); + + return STATUS_SUCCESS; +} + + +NTSTATUS EncryptedIoQueueStart (EncryptedIoQueue *queue) +{ + NTSTATUS status; + EncryptedIoQueueBuffer *buffer; + int i; + + queue->StartPending = TRUE; + queue->ThreadExitRequested = FALSE; + + queue->OutstandingIoCount = 0; + queue->IoThreadPendingRequestCount = 0; + + queue->FirstPoolBuffer = NULL; + KeInitializeMutex (&queue->BufferPoolMutex, 0); + + KeInitializeEvent (&queue->NoOutstandingIoEvent, SynchronizationEvent, FALSE); + KeInitializeEvent (&queue->PoolBufferFreeEvent, SynchronizationEvent, FALSE); + KeInitializeEvent (&queue->QueueResumedEvent, SynchronizationEvent, FALSE); + + queue->FragmentBufferA = TCalloc (TC_ENC_IO_QUEUE_MAX_FRAGMENT_SIZE); + if (!queue->FragmentBufferA) + goto noMemory; + + queue->FragmentBufferB = TCalloc (TC_ENC_IO_QUEUE_MAX_FRAGMENT_SIZE); + if (!queue->FragmentBufferB) + goto noMemory; + + KeInitializeEvent (&queue->FragmentBufferAFreeEvent, SynchronizationEvent, TRUE); + KeInitializeEvent (&queue->FragmentBufferBFreeEvent, SynchronizationEvent, TRUE); + + queue->ReadAheadBufferValid = FALSE; + queue->ReadAheadBuffer = TCalloc (TC_ENC_IO_QUEUE_MAX_FRAGMENT_SIZE); + if (!queue->ReadAheadBuffer) + goto noMemory; + + // Preallocate buffers + for (i = 0; i < TC_ENC_IO_QUEUE_PREALLOCATED_IO_REQUEST_COUNT; ++i) + { + if (i < TC_ENC_IO_QUEUE_PREALLOCATED_ITEM_COUNT && !GetPoolBuffer (queue, sizeof (EncryptedIoQueueItem))) + goto noMemory; + + if (!GetPoolBuffer (queue, sizeof (EncryptedIoRequest))) + goto noMemory; + } + + for (buffer = queue->FirstPoolBuffer; buffer != NULL; buffer = buffer->NextBuffer) + { + buffer->InUse = FALSE; + } + + // Main thread + InitializeListHead (&queue->MainThreadQueue); + KeInitializeSpinLock (&queue->MainThreadQueueLock); + KeInitializeEvent (&queue->MainThreadQueueNotEmptyEvent, SynchronizationEvent, FALSE); + + status = TCStartThread (MainThreadProc, queue, &queue->MainThread); + if (!NT_SUCCESS (status)) + goto err; + + // IO thread + InitializeListHead (&queue->IoThreadQueue); + KeInitializeSpinLock (&queue->IoThreadQueueLock); + KeInitializeEvent (&queue->IoThreadQueueNotEmptyEvent, SynchronizationEvent, FALSE); + + status = TCStartThread (IoThreadProc, queue, &queue->IoThread); + if (!NT_SUCCESS (status)) + { + queue->ThreadExitRequested = TRUE; + TCStopThread (queue->MainThread, &queue->MainThreadQueueNotEmptyEvent); + goto err; + } + + // Completion thread + InitializeListHead (&queue->CompletionThreadQueue); + KeInitializeSpinLock (&queue->CompletionThreadQueueLock); + KeInitializeEvent (&queue->CompletionThreadQueueNotEmptyEvent, SynchronizationEvent, FALSE); + + status = TCStartThread (CompletionThreadProc, queue, &queue->CompletionThread); + if (!NT_SUCCESS (status)) + { + queue->ThreadExitRequested = TRUE; + TCStopThread (queue->MainThread, &queue->MainThreadQueueNotEmptyEvent); + TCStopThread (queue->IoThread, &queue->IoThreadQueueNotEmptyEvent); + goto err; + } + +#ifdef TC_TRACE_IO_QUEUE + GetElapsedTimeInit (&queue->LastPerformanceCounter); +#endif + + queue->StopPending = FALSE; + queue->StartPending = FALSE; + + Dump ("Queue started\n"); + return STATUS_SUCCESS; + +noMemory: + status = STATUS_INSUFFICIENT_RESOURCES; + +err: + if (queue->FragmentBufferA) + TCfree (queue->FragmentBufferA); + if (queue->FragmentBufferB) + TCfree (queue->FragmentBufferB); + if (queue->ReadAheadBuffer) + TCfree (queue->ReadAheadBuffer); + + FreePoolBuffers (queue); + + queue->StartPending = FALSE; + return status; +} + + +NTSTATUS EncryptedIoQueueStop (EncryptedIoQueue *queue) +{ + ASSERT (!queue->StopPending); + queue->StopPending = TRUE; + + while (InterlockedExchangeAdd (&queue->OutstandingIoCount, 0) > 0) + { + KeWaitForSingleObject (&queue->NoOutstandingIoEvent, Executive, KernelMode, FALSE, NULL); + } + + Dump ("Queue stopping out=%d\n", queue->OutstandingIoCount); + + queue->ThreadExitRequested = TRUE; + + TCStopThread (queue->MainThread, &queue->MainThreadQueueNotEmptyEvent); + TCStopThread (queue->IoThread, &queue->IoThreadQueueNotEmptyEvent); + TCStopThread (queue->CompletionThread, &queue->CompletionThreadQueueNotEmptyEvent); + + TCfree (queue->FragmentBufferA); + TCfree (queue->FragmentBufferB); + TCfree (queue->ReadAheadBuffer); + + FreePoolBuffers (queue); + + Dump ("Queue stopped out=%d\n", queue->OutstandingIoCount); + return STATUS_SUCCESS; +} diff --git a/src/Driver/EncryptedIoQueue.h b/src/Driver/EncryptedIoQueue.h index de9fce07..044009b7 100644 --- a/src/Driver/EncryptedIoQueue.h +++ b/src/Driver/EncryptedIoQueue.h @@ -1,165 +1,165 @@ -/* - Derived from source code of TrueCrypt 7.1a, which is - Copyright (c) 2008-2012 TrueCrypt Developers Association and which is governed - by the TrueCrypt License 3.0. - - Modifications and additions to the original source code (contained in this file) - and all other portions of this file are Copyright (c) 2013-2016 IDRIX - and are governed by the Apache License 2.0 the full text of which is - contained in the file License.txt included in VeraCrypt binary and source - code distribution packages. -*/ - -#ifndef TC_HEADER_DRIVER_ENCRYPTED_IO_QUEUE -#define TC_HEADER_DRIVER_ENCRYPTED_IO_QUEUE - -#include "TCdefs.h" -#include "Apidrvr.h" - -#if 0 -# define TC_TRACE_IO_QUEUE -#endif - -#define TC_ENC_IO_QUEUE_MAX_FRAGMENT_SIZE (256 * 1024) - -#define TC_ENC_IO_QUEUE_PREALLOCATED_ITEM_COUNT 8 -#define TC_ENC_IO_QUEUE_PREALLOCATED_IO_REQUEST_COUNT 16 - - -typedef struct EncryptedIoQueueBufferStruct -{ - struct EncryptedIoQueueBufferStruct *NextBuffer; - - void *Address; - ULONG Size; - BOOL InUse; - -} EncryptedIoQueueBuffer; - - -typedef struct -{ - PDEVICE_OBJECT DeviceObject; - - KMUTEX BufferPoolMutex; - EncryptedIoQueueBuffer *FirstPoolBuffer; - - CRYPTO_INFO *CryptoInfo; - - // File-handle-based IO - HANDLE HostFileHandle; - int64 VirtualDeviceLength; - SECURITY_CLIENT_CONTEXT *SecurityClientContext; - - // Filter device - BOOL IsFilterDevice; - PDEVICE_OBJECT LowerDeviceObject; - int64 EncryptedAreaStart; - volatile int64 EncryptedAreaEnd; - volatile BOOL EncryptedAreaEndUpdatePending; - BOOL RemapEncryptedArea; - int64 RemappedAreaOffset; - int64 RemappedAreaDataUnitOffset; - IO_REMOVE_LOCK RemoveLock; - - // Main tread - PKTHREAD MainThread; - LIST_ENTRY MainThreadQueue; - KSPIN_LOCK MainThreadQueueLock; - KEVENT MainThreadQueueNotEmptyEvent; - - // IO thread - PKTHREAD IoThread; - LIST_ENTRY IoThreadQueue; - KSPIN_LOCK IoThreadQueueLock; - KEVENT IoThreadQueueNotEmptyEvent; - - // Completion thread - PKTHREAD CompletionThread; - LIST_ENTRY CompletionThreadQueue; - KSPIN_LOCK CompletionThreadQueueLock; - KEVENT CompletionThreadQueueNotEmptyEvent; - - // Fragment buffers - byte *FragmentBufferA; - byte *FragmentBufferB; - KEVENT FragmentBufferAFreeEvent; - KEVENT FragmentBufferBFreeEvent; - - // Read-ahead buffer - BOOL ReadAheadBufferValid; - LARGE_INTEGER LastReadOffset; - ULONG LastReadLength; - LARGE_INTEGER ReadAheadOffset; - ULONG ReadAheadLength; - byte *ReadAheadBuffer; - LARGE_INTEGER MaxReadAheadOffset; - - LONG OutstandingIoCount; - KEVENT NoOutstandingIoEvent; - LONG IoThreadPendingRequestCount; - - KEVENT PoolBufferFreeEvent; - - __int64 TotalBytesRead; - __int64 TotalBytesWritten; - - volatile BOOL StartPending; - volatile BOOL ThreadExitRequested; - - volatile BOOL Suspended; - volatile BOOL SuspendPending; - volatile BOOL StopPending; - - KEVENT QueueResumedEvent; - -#ifdef TC_TRACE_IO_QUEUE - LARGE_INTEGER LastPerformanceCounter; -#endif - -} EncryptedIoQueue; - - -typedef struct -{ - EncryptedIoQueue *Queue; - PIRP OriginalIrp; - BOOL Write; - ULONG OriginalLength; - LARGE_INTEGER OriginalOffset; - NTSTATUS Status; - -#ifdef TC_TRACE_IO_QUEUE - LARGE_INTEGER OriginalIrpOffset; -#endif - -} EncryptedIoQueueItem; - - -typedef struct -{ - EncryptedIoQueueItem *Item; - - BOOL CompleteOriginalIrp; - LARGE_INTEGER Offset; - ULONG Length; - int64 EncryptedOffset; - ULONG EncryptedLength; - byte *Data; - byte *OrigDataBufferFragment; - - LIST_ENTRY ListEntry; - LIST_ENTRY CompletionListEntry; -} EncryptedIoRequest; - - -NTSTATUS EncryptedIoQueueAddIrp (EncryptedIoQueue *queue, PIRP irp); -BOOL EncryptedIoQueueIsRunning (EncryptedIoQueue *queue); -BOOL EncryptedIoQueueIsSuspended (EncryptedIoQueue *queue); -NTSTATUS EncryptedIoQueueResumeFromHold (EncryptedIoQueue *queue); -NTSTATUS EncryptedIoQueueStart (EncryptedIoQueue *queue); -NTSTATUS EncryptedIoQueueStop (EncryptedIoQueue *queue); -NTSTATUS EncryptedIoQueueHoldWhenIdle (EncryptedIoQueue *queue, int64 timeout); - - -#endif // TC_HEADER_DRIVER_ENCRYPTED_IO_QUEUE +/* + Derived from source code of TrueCrypt 7.1a, which is + Copyright (c) 2008-2012 TrueCrypt Developers Association and which is governed + by the TrueCrypt License 3.0. + + Modifications and additions to the original source code (contained in this file) + and all other portions of this file are Copyright (c) 2013-2016 IDRIX + and are governed by the Apache License 2.0 the full text of which is + contained in the file License.txt included in VeraCrypt binary and source + code distribution packages. +*/ + +#ifndef TC_HEADER_DRIVER_ENCRYPTED_IO_QUEUE +#define TC_HEADER_DRIVER_ENCRYPTED_IO_QUEUE + +#include "TCdefs.h" +#include "Apidrvr.h" + +#if 0 +# define TC_TRACE_IO_QUEUE +#endif + +#define TC_ENC_IO_QUEUE_MAX_FRAGMENT_SIZE (256 * 1024) + +#define TC_ENC_IO_QUEUE_PREALLOCATED_ITEM_COUNT 8 +#define TC_ENC_IO_QUEUE_PREALLOCATED_IO_REQUEST_COUNT 16 + + +typedef struct EncryptedIoQueueBufferStruct +{ + struct EncryptedIoQueueBufferStruct *NextBuffer; + + void *Address; + ULONG Size; + BOOL InUse; + +} EncryptedIoQueueBuffer; + + +typedef struct +{ + PDEVICE_OBJECT DeviceObject; + + KMUTEX BufferPoolMutex; + EncryptedIoQueueBuffer *FirstPoolBuffer; + + CRYPTO_INFO *CryptoInfo; + + // File-handle-based IO + HANDLE HostFileHandle; + int64 VirtualDeviceLength; + SECURITY_CLIENT_CONTEXT *SecurityClientContext; + + // Filter device + BOOL IsFilterDevice; + PDEVICE_OBJECT LowerDeviceObject; + int64 EncryptedAreaStart; + volatile int64 EncryptedAreaEnd; + volatile BOOL EncryptedAreaEndUpdatePending; + BOOL RemapEncryptedArea; + int64 RemappedAreaOffset; + int64 RemappedAreaDataUnitOffset; + IO_REMOVE_LOCK RemoveLock; + + // Main tread + PKTHREAD MainThread; + LIST_ENTRY MainThreadQueue; + KSPIN_LOCK MainThreadQueueLock; + KEVENT MainThreadQueueNotEmptyEvent; + + // IO thread + PKTHREAD IoThread; + LIST_ENTRY IoThreadQueue; + KSPIN_LOCK IoThreadQueueLock; + KEVENT IoThreadQueueNotEmptyEvent; + + // Completion thread + PKTHREAD CompletionThread; + LIST_ENTRY CompletionThreadQueue; + KSPIN_LOCK CompletionThreadQueueLock; + KEVENT CompletionThreadQueueNotEmptyEvent; + + // Fragment buffers + byte *FragmentBufferA; + byte *FragmentBufferB; + KEVENT FragmentBufferAFreeEvent; + KEVENT FragmentBufferBFreeEvent; + + // Read-ahead buffer + BOOL ReadAheadBufferValid; + LARGE_INTEGER LastReadOffset; + ULONG LastReadLength; + LARGE_INTEGER ReadAheadOffset; + ULONG ReadAheadLength; + byte *ReadAheadBuffer; + LARGE_INTEGER MaxReadAheadOffset; + + LONG OutstandingIoCount; + KEVENT NoOutstandingIoEvent; + LONG IoThreadPendingRequestCount; + + KEVENT PoolBufferFreeEvent; + + __int64 TotalBytesRead; + __int64 TotalBytesWritten; + + volatile BOOL StartPending; + volatile BOOL ThreadExitRequested; + + volatile BOOL Suspended; + volatile BOOL SuspendPending; + volatile BOOL StopPending; + + KEVENT QueueResumedEvent; + +#ifdef TC_TRACE_IO_QUEUE + LARGE_INTEGER LastPerformanceCounter; +#endif + +} EncryptedIoQueue; + + +typedef struct +{ + EncryptedIoQueue *Queue; + PIRP OriginalIrp; + BOOL Write; + ULONG OriginalLength; + LARGE_INTEGER OriginalOffset; + NTSTATUS Status; + +#ifdef TC_TRACE_IO_QUEUE + LARGE_INTEGER OriginalIrpOffset; +#endif + +} EncryptedIoQueueItem; + + +typedef struct +{ + EncryptedIoQueueItem *Item; + + BOOL CompleteOriginalIrp; + LARGE_INTEGER Offset; + ULONG Length; + int64 EncryptedOffset; + ULONG EncryptedLength; + byte *Data; + byte *OrigDataBufferFragment; + + LIST_ENTRY ListEntry; + LIST_ENTRY CompletionListEntry; +} EncryptedIoRequest; + + +NTSTATUS EncryptedIoQueueAddIrp (EncryptedIoQueue *queue, PIRP irp); +BOOL EncryptedIoQueueIsRunning (EncryptedIoQueue *queue); +BOOL EncryptedIoQueueIsSuspended (EncryptedIoQueue *queue); +NTSTATUS EncryptedIoQueueResumeFromHold (EncryptedIoQueue *queue); +NTSTATUS EncryptedIoQueueStart (EncryptedIoQueue *queue); +NTSTATUS EncryptedIoQueueStop (EncryptedIoQueue *queue); +NTSTATUS EncryptedIoQueueHoldWhenIdle (EncryptedIoQueue *queue, int64 timeout); + + +#endif // TC_HEADER_DRIVER_ENCRYPTED_IO_QUEUE diff --git a/src/Driver/Makefile b/src/Driver/Makefile index 53b9a3d6..5acbbd24 100644 --- a/src/Driver/Makefile +++ b/src/Driver/Makefile @@ -1 +1 @@ -!INCLUDE $(NTMAKEENV)\makefile.def +!INCLUDE $(NTMAKEENV)\makefile.def diff --git a/src/Driver/Ntdriver.c b/src/Driver/Ntdriver.c index eeea7815..c771b3ce 100644 --- a/src/Driver/Ntdriver.c +++ b/src/Driver/Ntdriver.c @@ -1,3563 +1,3563 @@ -/* - Legal Notice: Some portions of the source code contained in this file were - derived from the source code of TrueCrypt 7.1a, which is - Copyright (c) 2003-2012 TrueCrypt Developers Association and which is - governed by the TrueCrypt License 3.0, also from the source code of - Encryption for the Masses 2.02a, which is Copyright (c) 1998-2000 Paul Le Roux - and which is governed by the 'License Agreement for Encryption for the Masses' - Modifications and additions to the original source code (contained in this file) - and all other portions of this file are Copyright (c) 2013-2016 IDRIX - and are governed by the Apache License 2.0 the full text of which is - contained in the file License.txt included in VeraCrypt binary and source - code distribution packages. */ - -#include "TCdefs.h" -#include -#include "Crypto.h" -#include "Fat.h" -#include "Tests.h" -#include "cpu.h" - -#include "Apidrvr.h" -#include "Boot/Windows/BootDefs.h" -#include "EncryptedIoQueue.h" -#include "EncryptionThreadPool.h" -#include "Ntdriver.h" -#include "Ntvol.h" -#include "DriveFilter.h" -#include "DumpFilter.h" -#include "Cache.h" -#include "Volumes.h" -#include "VolumeFilter.h" - -#include -#include -#include -#include -#include - -#include -#include - -/* Init section, which is thrown away as soon as DriverEntry returns */ -#pragma alloc_text(INIT,DriverEntry) -#pragma alloc_text(INIT,TCCreateRootDeviceObject) - -PDRIVER_OBJECT TCDriverObject; -PDEVICE_OBJECT RootDeviceObject = NULL; -static KMUTEX RootDeviceControlMutex; -BOOL DriverShuttingDown = FALSE; -BOOL SelfTestsPassed; -int LastUniqueVolumeId; -ULONG OsMajorVersion = 0; -ULONG OsMinorVersion; -BOOL DriverUnloadDisabled = FALSE; -BOOL PortableMode = FALSE; -BOOL VolumeClassFilterRegistered = FALSE; -BOOL CacheBootPassword = FALSE; -BOOL CacheBootPim = FALSE; -BOOL NonAdminSystemFavoritesAccessDisabled = FALSE; -static size_t EncryptionThreadPoolFreeCpuCountLimit = 0; -static BOOL SystemFavoriteVolumeDirty = FALSE; -static BOOL PagingFileCreationPrevented = FALSE; -static BOOL EnableExtendedIoctlSupport = FALSE; - -PDEVICE_OBJECT VirtualVolumeDeviceObjects[MAX_MOUNTED_VOLUME_DRIVE_NUMBER + 1]; - - -NTSTATUS DriverEntry (PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegistryPath) -{ - PKEY_VALUE_PARTIAL_INFORMATION startKeyValue; - LONG version; - int i; - - Dump ("DriverEntry " TC_APP_NAME " " VERSION_STRING "\n"); - - DetectX86Features (); - - PsGetVersion (&OsMajorVersion, &OsMinorVersion, NULL, NULL); - - // Load dump filter if the main driver is already loaded - if (NT_SUCCESS (TCDeviceIoControl (NT_ROOT_PREFIX, TC_IOCTL_GET_DRIVER_VERSION, NULL, 0, &version, sizeof (version)))) - return DumpFilterEntry ((PFILTER_EXTENSION) DriverObject, (PFILTER_INITIALIZATION_DATA) RegistryPath); - - TCDriverObject = DriverObject; - memset (VirtualVolumeDeviceObjects, 0, sizeof (VirtualVolumeDeviceObjects)); - - ReadRegistryConfigFlags (TRUE); - EncryptionThreadPoolStart (EncryptionThreadPoolFreeCpuCountLimit); - SelfTestsPassed = AutoTestAlgorithms(); - - // Enable device class filters and load boot arguments if the driver is set to start at system boot - - if (NT_SUCCESS (TCReadRegistryKey (RegistryPath, L"Start", &startKeyValue))) - { - if (startKeyValue->Type == REG_DWORD && *((uint32 *) startKeyValue->Data) == SERVICE_BOOT_START) - { - if (!SelfTestsPassed) - TC_BUG_CHECK (STATUS_INVALID_PARAMETER); - - LoadBootArguments(); - VolumeClassFilterRegistered = IsVolumeClassFilterRegistered(); - - DriverObject->DriverExtension->AddDevice = DriverAddDevice; - } - - TCfree (startKeyValue); - } - - for (i = 0; i <= IRP_MJ_MAXIMUM_FUNCTION; ++i) - { - DriverObject->MajorFunction[i] = TCDispatchQueueIRP; - } - - DriverObject->DriverUnload = TCUnloadDriver; - return TCCreateRootDeviceObject (DriverObject); -} - - -NTSTATUS DriverAddDevice (PDRIVER_OBJECT driverObject, PDEVICE_OBJECT pdo) -{ -#ifdef DEBUG - char nameInfoBuffer[128]; - POBJECT_NAME_INFORMATION nameInfo = (POBJECT_NAME_INFORMATION) nameInfoBuffer; - ULONG nameInfoSize; - Dump ("AddDevice pdo=%p type=%x name=%ws\n", pdo, pdo->DeviceType, NT_SUCCESS (ObQueryNameString (pdo, nameInfo, sizeof (nameInfoBuffer), &nameInfoSize)) ? nameInfo->Name.Buffer : L"?"); -#endif - - if (VolumeClassFilterRegistered && BootArgsValid && BootArgs.HiddenSystemPartitionStart != 0) - { - PWSTR interfaceLinks = NULL; - if (NT_SUCCESS (IoGetDeviceInterfaces (&GUID_DEVINTERFACE_VOLUME, pdo, DEVICE_INTERFACE_INCLUDE_NONACTIVE, &interfaceLinks)) && interfaceLinks) - { - if (interfaceLinks[0] != UNICODE_NULL) - { - Dump ("Volume pdo=%p interface=%ws\n", pdo, interfaceLinks); - ExFreePool (interfaceLinks); - - return VolumeFilterAddDevice (driverObject, pdo); - } - - ExFreePool (interfaceLinks); - } - } - - return DriveFilterAddDevice (driverObject, pdo); -} - - -// Dumps a memory region to debug output -void DumpMemory (void *mem, int size) -{ - unsigned char str[20]; - unsigned char *m = mem; - int i,j; - - for (j = 0; j < size / 8; j++) - { - memset (str,0,sizeof str); - for (i = 0; i < 8; i++) - { - if (m[i] > ' ' && m[i] <= '~') - str[i]=m[i]; - else - str[i]='.'; - } - - Dump ("0x%08p %02x %02x %02x %02x %02x %02x %02x %02x %s\n", - m, m[0], m[1], m[2], m[3], m[4], m[5], m[6], m[7], str); - - m+=8; - } -} - - -BOOL ValidateIOBufferSize (PIRP irp, size_t requiredBufferSize, ValidateIOBufferSizeType type) -{ - PIO_STACK_LOCATION irpSp = IoGetCurrentIrpStackLocation (irp); - BOOL input = (type == ValidateInput || type == ValidateInputOutput); - BOOL output = (type == ValidateOutput || type == ValidateInputOutput); - - if ((input && irpSp->Parameters.DeviceIoControl.InputBufferLength < requiredBufferSize) - || (output && irpSp->Parameters.DeviceIoControl.OutputBufferLength < requiredBufferSize)) - { - Dump ("STATUS_BUFFER_TOO_SMALL ioctl=0x%x,%d in=%d out=%d reqsize=%d insize=%d outsize=%d\n", (int) (irpSp->Parameters.DeviceIoControl.IoControlCode >> 16), (int) ((irpSp->Parameters.DeviceIoControl.IoControlCode & 0x1FFF) >> 2), input, output, requiredBufferSize, irpSp->Parameters.DeviceIoControl.InputBufferLength, irpSp->Parameters.DeviceIoControl.OutputBufferLength); - - irp->IoStatus.Status = STATUS_BUFFER_TOO_SMALL; - irp->IoStatus.Information = 0; - return FALSE; - } - - if (!input && output) - memset (irp->AssociatedIrp.SystemBuffer, 0, irpSp->Parameters.DeviceIoControl.OutputBufferLength); - - return TRUE; -} - - -PDEVICE_OBJECT GetVirtualVolumeDeviceObject (int driveNumber) -{ - if (driveNumber < MIN_MOUNTED_VOLUME_DRIVE_NUMBER || driveNumber > MAX_MOUNTED_VOLUME_DRIVE_NUMBER) - return NULL; - - return VirtualVolumeDeviceObjects[driveNumber]; -} - - -/* TCDispatchQueueIRP queues any IRP's so that they can be processed later - by the thread -- or in some cases handles them immediately! */ -NTSTATUS TCDispatchQueueIRP (PDEVICE_OBJECT DeviceObject, PIRP Irp) -{ - PEXTENSION Extension = (PEXTENSION) DeviceObject->DeviceExtension; - PIO_STACK_LOCATION irpSp = IoGetCurrentIrpStackLocation (Irp); - NTSTATUS ntStatus; - -#ifdef _DEBUG - if (irpSp->MajorFunction == IRP_MJ_DEVICE_CONTROL && (Extension->bRootDevice || Extension->IsVolumeDevice)) - { - switch (irpSp->Parameters.DeviceIoControl.IoControlCode) - { - case TC_IOCTL_GET_MOUNTED_VOLUMES: - case TC_IOCTL_GET_PASSWORD_CACHE_STATUS: - case TC_IOCTL_GET_PORTABLE_MODE_STATUS: - case TC_IOCTL_SET_PORTABLE_MODE_STATUS: - case TC_IOCTL_OPEN_TEST: - case TC_IOCTL_GET_RESOLVED_SYMLINK: - case TC_IOCTL_GET_DEVICE_REFCOUNT: - case TC_IOCTL_GET_DRIVE_PARTITION_INFO: - case TC_IOCTL_GET_BOOT_DRIVE_VOLUME_PROPERTIES: - case TC_IOCTL_GET_BOOT_ENCRYPTION_STATUS: - case TC_IOCTL_GET_DECOY_SYSTEM_WIPE_STATUS: - case TC_IOCTL_GET_WARNING_FLAGS: - case TC_IOCTL_IS_HIDDEN_SYSTEM_RUNNING: - case IOCTL_DISK_CHECK_VERIFY: - break; - - default: - Dump ("%ls (0x%x %d)\n", - TCTranslateCode (irpSp->Parameters.DeviceIoControl.IoControlCode), - (int) (irpSp->Parameters.DeviceIoControl.IoControlCode >> 16), - (int) ((irpSp->Parameters.DeviceIoControl.IoControlCode & 0x1FFF) >> 2)); - } - } -#endif - - if (!Extension->bRootDevice) - { - // Drive filter IRP - if (Extension->IsDriveFilterDevice) - return DriveFilterDispatchIrp (DeviceObject, Irp); - - // Volume filter IRP - if (Extension->IsVolumeFilterDevice) - return VolumeFilterDispatchIrp (DeviceObject, Irp); - } - - switch (irpSp->MajorFunction) - { - case IRP_MJ_CLOSE: - case IRP_MJ_CREATE: - case IRP_MJ_CLEANUP: - return COMPLETE_IRP (DeviceObject, Irp, STATUS_SUCCESS, 0); - - case IRP_MJ_SHUTDOWN: - if (Extension->bRootDevice) - { - Dump ("Driver shutting down\n"); - DriverShuttingDown = TRUE; - - if (EncryptionSetupThread) - while (SendDeviceIoControlRequest (RootDeviceObject, TC_IOCTL_ABORT_BOOT_ENCRYPTION_SETUP, NULL, 0, NULL, 0) == STATUS_INSUFFICIENT_RESOURCES); - - if (DecoySystemWipeThread) - while (SendDeviceIoControlRequest (RootDeviceObject, TC_IOCTL_ABORT_DECOY_SYSTEM_WIPE, NULL, 0, NULL, 0) == STATUS_INSUFFICIENT_RESOURCES); - - OnShutdownPending(); - } - - return COMPLETE_IRP (DeviceObject, Irp, STATUS_SUCCESS, 0); - - case IRP_MJ_FLUSH_BUFFERS: - case IRP_MJ_READ: - case IRP_MJ_WRITE: - case IRP_MJ_DEVICE_CONTROL: - - if (Extension->bRootDevice) - { - if (irpSp->MajorFunction == IRP_MJ_DEVICE_CONTROL) - { - NTSTATUS status = KeWaitForMutexObject (&RootDeviceControlMutex, Executive, KernelMode, FALSE, NULL); - if (!NT_SUCCESS (status)) - return status; - - status = ProcessMainDeviceControlIrp (DeviceObject, Extension, Irp); - - KeReleaseMutex (&RootDeviceControlMutex, FALSE); - return status; - } - break; - } - - if (Extension->bShuttingDown) - { - Dump ("Device %d shutting down: STATUS_DELETE_PENDING\n", Extension->nDosDriveNo); - return TCCompleteDiskIrp (Irp, STATUS_DELETE_PENDING, 0); - } - - if (Extension->bRemovable - && (DeviceObject->Flags & DO_VERIFY_VOLUME) - && !(irpSp->Flags & SL_OVERRIDE_VERIFY_VOLUME) - && irpSp->MajorFunction != IRP_MJ_FLUSH_BUFFERS) - { - Dump ("Removable device %d has DO_VERIFY_VOLUME flag: STATUS_DEVICE_NOT_READY\n", Extension->nDosDriveNo); - return TCCompleteDiskIrp (Irp, STATUS_DEVICE_NOT_READY, 0); - } - - switch (irpSp->MajorFunction) - { - case IRP_MJ_READ: - case IRP_MJ_WRITE: - ntStatus = EncryptedIoQueueAddIrp (&Extension->Queue, Irp); - - if (ntStatus != STATUS_PENDING) - TCCompleteDiskIrp (Irp, ntStatus, 0); - - return ntStatus; - - case IRP_MJ_DEVICE_CONTROL: - ntStatus = IoAcquireRemoveLock (&Extension->Queue.RemoveLock, Irp); - if (!NT_SUCCESS (ntStatus)) - return TCCompleteIrp (Irp, ntStatus, 0); - - IoMarkIrpPending (Irp); - - ExInterlockedInsertTailList (&Extension->ListEntry, &Irp->Tail.Overlay.ListEntry, &Extension->ListSpinLock); - KeReleaseSemaphore (&Extension->RequestSemaphore, IO_DISK_INCREMENT, 1, FALSE); - - return STATUS_PENDING; - - case IRP_MJ_FLUSH_BUFFERS: - return TCCompleteDiskIrp (Irp, STATUS_SUCCESS, 0); - } - - break; - - case IRP_MJ_PNP: - if (!Extension->bRootDevice - && Extension->IsVolumeDevice - && irpSp->MinorFunction == IRP_MN_DEVICE_USAGE_NOTIFICATION - && irpSp->Parameters.UsageNotification.Type == DeviceUsageTypePaging - && irpSp->Parameters.UsageNotification.InPath) - { - PagingFileCreationPrevented = TRUE; - return TCCompleteIrp (Irp, STATUS_UNSUCCESSFUL, 0); - } - break; - } - - return TCCompleteIrp (Irp, STATUS_INVALID_DEVICE_REQUEST, 0); -} - -NTSTATUS TCCreateRootDeviceObject (PDRIVER_OBJECT DriverObject) -{ - UNICODE_STRING Win32NameString, ntUnicodeString; - WCHAR dosname[32], ntname[32]; - PDEVICE_OBJECT DeviceObject; - NTSTATUS ntStatus; - BOOL *bRootExtension; - - Dump ("TCCreateRootDeviceObject BEGIN\n"); - ASSERT (KeGetCurrentIrql() == PASSIVE_LEVEL); - - RtlStringCbCopyW (dosname, sizeof(dosname),(LPWSTR) DOS_ROOT_PREFIX); - RtlStringCbCopyW (ntname, sizeof(ntname),(LPWSTR) NT_ROOT_PREFIX); - RtlInitUnicodeString (&ntUnicodeString, ntname); - RtlInitUnicodeString (&Win32NameString, dosname); - - Dump ("Creating root device nt=%ls dos=%ls\n", ntname, dosname); - - ntStatus = IoCreateDevice ( - DriverObject, - sizeof (BOOL), - &ntUnicodeString, - FILE_DEVICE_UNKNOWN, - FILE_DEVICE_SECURE_OPEN, - FALSE, - &DeviceObject); - - if (!NT_SUCCESS (ntStatus)) - { - Dump ("TCCreateRootDeviceObject NTSTATUS = 0x%08x END\n", ntStatus); - return ntStatus;/* Failed to create DeviceObject */ - } - - DeviceObject->Flags |= DO_DIRECT_IO; - DeviceObject->AlignmentRequirement = FILE_WORD_ALIGNMENT; - - /* Setup the device extension */ - bRootExtension = (BOOL *) DeviceObject->DeviceExtension; - *bRootExtension = TRUE; - - KeInitializeMutex (&RootDeviceControlMutex, 0); - - ntStatus = IoCreateSymbolicLink (&Win32NameString, &ntUnicodeString); - - if (!NT_SUCCESS (ntStatus)) - { - Dump ("TCCreateRootDeviceObject NTSTATUS = 0x%08x END\n", ntStatus); - IoDeleteDevice (DeviceObject); - return ntStatus; - } - - IoRegisterShutdownNotification (DeviceObject); - RootDeviceObject = DeviceObject; - - Dump ("TCCreateRootDeviceObject STATUS_SUCCESS END\n"); - return STATUS_SUCCESS; -} - -NTSTATUS TCCreateDeviceObject (PDRIVER_OBJECT DriverObject, - PDEVICE_OBJECT * ppDeviceObject, - MOUNT_STRUCT * mount) -{ - UNICODE_STRING ntUnicodeString; - WCHAR ntname[32]; - PEXTENSION Extension; - NTSTATUS ntStatus; - ULONG devChars = 0; -#if defined (DEBUG) - WCHAR dosname[32]; -#endif - - Dump ("TCCreateDeviceObject BEGIN\n"); - ASSERT (KeGetCurrentIrql() == PASSIVE_LEVEL); - - TCGetNTNameFromNumber (ntname, sizeof(ntname),mount->nDosDriveNo); - RtlInitUnicodeString (&ntUnicodeString, ntname); -#if defined (DEBUG) - TCGetDosNameFromNumber (dosname, sizeof(dosname),mount->nDosDriveNo, DeviceNamespaceDefault); -#endif - - devChars = FILE_DEVICE_SECURE_OPEN; - devChars |= mount->bMountReadOnly ? FILE_READ_ONLY_DEVICE : 0; - devChars |= mount->bMountRemovable ? FILE_REMOVABLE_MEDIA : 0; - - Dump ("Creating device nt=%ls dos=%ls\n", ntname, dosname); - - ntStatus = IoCreateDevice ( - DriverObject, /* Our Driver Object */ - sizeof (EXTENSION), /* Size of state information */ - &ntUnicodeString, /* Device name "\Device\Name" */ - FILE_DEVICE_DISK, /* Device type */ - devChars, /* Device characteristics */ - FALSE, /* Exclusive device */ - ppDeviceObject); /* Returned ptr to Device Object */ - - if (!NT_SUCCESS (ntStatus)) - { - Dump ("TCCreateDeviceObject NTSTATUS = 0x%08x END\n", ntStatus); - return ntStatus;/* Failed to create DeviceObject */ - } - /* Initialize device object and extension. */ - - (*ppDeviceObject)->Flags |= DO_DIRECT_IO; - (*ppDeviceObject)->StackSize += 6; // Reduce occurrence of NO_MORE_IRP_STACK_LOCATIONS bug check caused by buggy drivers - - /* Setup the device extension */ - Extension = (PEXTENSION) (*ppDeviceObject)->DeviceExtension; - memset (Extension, 0, sizeof (EXTENSION)); - - Extension->IsVolumeDevice = TRUE; - Extension->nDosDriveNo = mount->nDosDriveNo; - Extension->bRemovable = mount->bMountRemovable; - Extension->PartitionInInactiveSysEncScope = mount->bPartitionInInactiveSysEncScope; - Extension->SystemFavorite = mount->SystemFavorite; - - KeInitializeEvent (&Extension->keCreateEvent, SynchronizationEvent, FALSE); - KeInitializeSemaphore (&Extension->RequestSemaphore, 0L, MAXLONG); - KeInitializeSpinLock (&Extension->ListSpinLock); - InitializeListHead (&Extension->ListEntry); - IoInitializeRemoveLock (&Extension->Queue.RemoveLock, 'LRCV', 0, 0); - - VirtualVolumeDeviceObjects[mount->nDosDriveNo] = *ppDeviceObject; - - Dump ("TCCreateDeviceObject STATUS_SUCCESS END\n"); - - return STATUS_SUCCESS; -} - - -BOOL RootDeviceControlMutexAcquireNoWait () -{ - NTSTATUS status; - LARGE_INTEGER timeout; - timeout.QuadPart = 0; - - status = KeWaitForMutexObject (&RootDeviceControlMutex, Executive, KernelMode, FALSE, &timeout); - return NT_SUCCESS (status) && status != STATUS_TIMEOUT; -} - - -void RootDeviceControlMutexRelease () -{ - KeReleaseMutex (&RootDeviceControlMutex, FALSE); -} - - -NTSTATUS ProcessVolumeDeviceControlIrp (PDEVICE_OBJECT DeviceObject, PEXTENSION Extension, PIRP Irp) -{ - PIO_STACK_LOCATION irpSp = IoGetCurrentIrpStackLocation (Irp); - - switch (irpSp->Parameters.DeviceIoControl.IoControlCode) - { - - case IOCTL_MOUNTDEV_QUERY_DEVICE_NAME: - if (!ValidateIOBufferSize (Irp, sizeof (MOUNTDEV_NAME), ValidateOutput)) - { - Irp->IoStatus.Information = sizeof (MOUNTDEV_NAME); - Irp->IoStatus.Status = STATUS_BUFFER_OVERFLOW; - } - else - { - ULONG outLength; - UNICODE_STRING ntUnicodeString; - WCHAR ntName[256]; - PMOUNTDEV_NAME outputBuffer = (PMOUNTDEV_NAME) Irp->AssociatedIrp.SystemBuffer; - - TCGetNTNameFromNumber (ntName, sizeof(ntName),Extension->nDosDriveNo); - RtlInitUnicodeString (&ntUnicodeString, ntName); - - outputBuffer->NameLength = ntUnicodeString.Length; - outLength = ntUnicodeString.Length + sizeof(USHORT); - - if (irpSp->Parameters.DeviceIoControl.OutputBufferLength < outLength) - { - Irp->IoStatus.Information = sizeof (MOUNTDEV_NAME); - Irp->IoStatus.Status = STATUS_BUFFER_OVERFLOW; - - break; - } - - RtlCopyMemory ((PCHAR)outputBuffer->Name,ntUnicodeString.Buffer, ntUnicodeString.Length); - - Irp->IoStatus.Status = STATUS_SUCCESS; - Irp->IoStatus.Information = outLength; - - Dump ("name = %ls\n",ntName); - } - break; - - case IOCTL_MOUNTDEV_QUERY_UNIQUE_ID: - if (!ValidateIOBufferSize (Irp, sizeof (MOUNTDEV_UNIQUE_ID), ValidateOutput)) - { - Irp->IoStatus.Information = sizeof (MOUNTDEV_UNIQUE_ID); - Irp->IoStatus.Status = STATUS_BUFFER_OVERFLOW; - } - else - { - ULONG outLength; - UCHAR volId[128], tmp[] = { 0,0 }; - PMOUNTDEV_UNIQUE_ID outputBuffer = (PMOUNTDEV_UNIQUE_ID) Irp->AssociatedIrp.SystemBuffer; - - RtlStringCbCopyA (volId, sizeof(volId),TC_UNIQUE_ID_PREFIX); - tmp[0] = 'A' + (UCHAR) Extension->nDosDriveNo; - RtlStringCbCatA (volId, sizeof(volId),tmp); - - outputBuffer->UniqueIdLength = (USHORT) strlen (volId); - outLength = (ULONG) (strlen (volId) + sizeof (USHORT)); - - if (irpSp->Parameters.DeviceIoControl.OutputBufferLength < outLength) - { - Irp->IoStatus.Information = sizeof (MOUNTDEV_UNIQUE_ID); - Irp->IoStatus.Status = STATUS_BUFFER_OVERFLOW; - break; - } - - RtlCopyMemory ((PCHAR)outputBuffer->UniqueId, volId, strlen (volId)); - - Irp->IoStatus.Status = STATUS_SUCCESS; - Irp->IoStatus.Information = outLength; - - Dump ("id = %s\n",volId); - } - break; - - case IOCTL_MOUNTDEV_QUERY_SUGGESTED_LINK_NAME: - { - ULONG outLength; - UNICODE_STRING ntUnicodeString; - WCHAR ntName[256]; - PMOUNTDEV_SUGGESTED_LINK_NAME outputBuffer = (PMOUNTDEV_SUGGESTED_LINK_NAME) Irp->AssociatedIrp.SystemBuffer; - - if (!ValidateIOBufferSize (Irp, sizeof (MOUNTDEV_SUGGESTED_LINK_NAME), ValidateOutput)) - { - Irp->IoStatus.Status = STATUS_INVALID_PARAMETER; - Irp->IoStatus.Information = 0; - break; - } - - TCGetDosNameFromNumber (ntName, sizeof(ntName),Extension->nDosDriveNo, DeviceNamespaceDefault); - RtlInitUnicodeString (&ntUnicodeString, ntName); - - outLength = FIELD_OFFSET(MOUNTDEV_SUGGESTED_LINK_NAME,Name) + ntUnicodeString.Length; - - outputBuffer->UseOnlyIfThereAreNoOtherLinks = FALSE; - outputBuffer->NameLength = ntUnicodeString.Length; - - if(irpSp->Parameters.DeviceIoControl.OutputBufferLength < outLength) - { - Irp->IoStatus.Information = sizeof (MOUNTDEV_SUGGESTED_LINK_NAME); - Irp->IoStatus.Status = STATUS_BUFFER_OVERFLOW; - break; - } - - RtlCopyMemory ((PCHAR)outputBuffer->Name,ntUnicodeString.Buffer, ntUnicodeString.Length); - - Irp->IoStatus.Status = STATUS_SUCCESS; - Irp->IoStatus.Information = outLength; - - Dump ("link = %ls\n",ntName); - } - break; - - case IOCTL_DISK_GET_MEDIA_TYPES: - case IOCTL_DISK_GET_DRIVE_GEOMETRY: - /* Return the drive geometry for the disk. Note that we - return values which were made up to suit the disk size. */ - if (ValidateIOBufferSize (Irp, sizeof (DISK_GEOMETRY), ValidateOutput)) - { - PDISK_GEOMETRY outputBuffer = (PDISK_GEOMETRY) - Irp->AssociatedIrp.SystemBuffer; - - outputBuffer->MediaType = Extension->bRemovable ? RemovableMedia : FixedMedia; - outputBuffer->Cylinders.QuadPart = Extension->NumberOfCylinders; - outputBuffer->TracksPerCylinder = Extension->TracksPerCylinder; - outputBuffer->SectorsPerTrack = Extension->SectorsPerTrack; - outputBuffer->BytesPerSector = Extension->BytesPerSector; - Irp->IoStatus.Status = STATUS_SUCCESS; - Irp->IoStatus.Information = sizeof (DISK_GEOMETRY); - } - break; - - case IOCTL_STORAGE_QUERY_PROPERTY: - if (EnableExtendedIoctlSupport) - { - if (ValidateIOBufferSize (Irp, sizeof (STORAGE_PROPERTY_QUERY), ValidateInput)) - { - PSTORAGE_PROPERTY_QUERY pStoragePropQuery = (PSTORAGE_PROPERTY_QUERY) Irp->AssociatedIrp.SystemBuffer; - STORAGE_QUERY_TYPE type = pStoragePropQuery->QueryType; - - /* return error if an unsupported type is encountered */ - Irp->IoStatus.Status = STATUS_INVALID_DEVICE_REQUEST; - Irp->IoStatus.Information = 0; - - if ( (pStoragePropQuery->PropertyId == StorageAccessAlignmentProperty) - || (pStoragePropQuery->PropertyId == StorageDeviceProperty) - ) - { - if (type == PropertyExistsQuery) - { - Irp->IoStatus.Status = STATUS_SUCCESS; - Irp->IoStatus.Information = 0; - } - else if (type == PropertyStandardQuery) - { - switch (pStoragePropQuery->PropertyId) - { - case StorageDeviceProperty: - { - if (ValidateIOBufferSize (Irp, sizeof (STORAGE_DEVICE_DESCRIPTOR), ValidateOutput)) - { - PSTORAGE_DEVICE_DESCRIPTOR outputBuffer = (PSTORAGE_DEVICE_DESCRIPTOR) Irp->AssociatedIrp.SystemBuffer; - - outputBuffer->Version = sizeof(STORAGE_DEVICE_DESCRIPTOR); - outputBuffer->Size = sizeof(STORAGE_DEVICE_DESCRIPTOR); - outputBuffer->DeviceType = FILE_DEVICE_DISK; - outputBuffer->RemovableMedia = Extension->bRemovable? TRUE : FALSE; - Irp->IoStatus.Status = STATUS_SUCCESS; - Irp->IoStatus.Information = sizeof (STORAGE_DEVICE_DESCRIPTOR); - } - } - break; - case StorageAccessAlignmentProperty: - { - if (ValidateIOBufferSize (Irp, sizeof (STORAGE_ACCESS_ALIGNMENT_DESCRIPTOR), ValidateOutput)) - { - PSTORAGE_ACCESS_ALIGNMENT_DESCRIPTOR outputBuffer = (PSTORAGE_ACCESS_ALIGNMENT_DESCRIPTOR) Irp->AssociatedIrp.SystemBuffer; - - outputBuffer->Version = sizeof(STORAGE_ACCESS_ALIGNMENT_DESCRIPTOR); - outputBuffer->Size = sizeof(STORAGE_ACCESS_ALIGNMENT_DESCRIPTOR); - outputBuffer->BytesPerLogicalSector = Extension->BytesPerSector; - outputBuffer->BytesPerPhysicalSector = Extension->HostBytesPerPhysicalSector; - outputBuffer->BytesOffsetForSectorAlignment = Extension->BytesOffsetForSectorAlignment; - Irp->IoStatus.Status = STATUS_SUCCESS; - Irp->IoStatus.Information = sizeof (STORAGE_ACCESS_ALIGNMENT_DESCRIPTOR); - } - } - break; - } - } - } - } - } - else - return TCCompleteIrp (Irp, STATUS_INVALID_DEVICE_REQUEST, 0); - - break; - - case IOCTL_DISK_GET_PARTITION_INFO: - if (ValidateIOBufferSize (Irp, sizeof (PARTITION_INFORMATION), ValidateOutput)) - { - PPARTITION_INFORMATION outputBuffer = (PPARTITION_INFORMATION) - Irp->AssociatedIrp.SystemBuffer; - - outputBuffer->PartitionType = Extension->PartitionType; - outputBuffer->BootIndicator = FALSE; - outputBuffer->RecognizedPartition = TRUE; - outputBuffer->RewritePartition = FALSE; - outputBuffer->StartingOffset.QuadPart = Extension->BytesPerSector; - outputBuffer->PartitionLength.QuadPart= Extension->DiskLength; - outputBuffer->HiddenSectors = 0; - Irp->IoStatus.Status = STATUS_SUCCESS; - Irp->IoStatus.Information = sizeof (PARTITION_INFORMATION); - } - break; - - case IOCTL_DISK_GET_PARTITION_INFO_EX: - if (ValidateIOBufferSize (Irp, sizeof (PARTITION_INFORMATION_EX), ValidateOutput)) - { - PPARTITION_INFORMATION_EX outputBuffer = (PPARTITION_INFORMATION_EX) Irp->AssociatedIrp.SystemBuffer; - - outputBuffer->PartitionStyle = PARTITION_STYLE_MBR; - outputBuffer->RewritePartition = FALSE; - outputBuffer->StartingOffset.QuadPart = Extension->BytesPerSector; - outputBuffer->PartitionLength.QuadPart= Extension->DiskLength; - outputBuffer->Mbr.PartitionType = Extension->PartitionType; - outputBuffer->Mbr.BootIndicator = FALSE; - outputBuffer->Mbr.RecognizedPartition = TRUE; - outputBuffer->Mbr.HiddenSectors = 0; - Irp->IoStatus.Status = STATUS_SUCCESS; - Irp->IoStatus.Information = sizeof (PARTITION_INFORMATION_EX); - } - break; - - case IOCTL_DISK_GET_DRIVE_LAYOUT: - if (ValidateIOBufferSize (Irp, sizeof (DRIVE_LAYOUT_INFORMATION), ValidateOutput)) - { - PDRIVE_LAYOUT_INFORMATION outputBuffer = (PDRIVE_LAYOUT_INFORMATION) - Irp->AssociatedIrp.SystemBuffer; - - outputBuffer->PartitionCount = 1; - outputBuffer->Signature = 0; - - outputBuffer->PartitionEntry->PartitionType = Extension->PartitionType; - outputBuffer->PartitionEntry->BootIndicator = FALSE; - outputBuffer->PartitionEntry->RecognizedPartition = TRUE; - outputBuffer->PartitionEntry->RewritePartition = FALSE; - outputBuffer->PartitionEntry->StartingOffset.QuadPart = Extension->BytesPerSector; - outputBuffer->PartitionEntry->PartitionLength.QuadPart = Extension->DiskLength; - outputBuffer->PartitionEntry->HiddenSectors = 0; - - Irp->IoStatus.Status = STATUS_SUCCESS; - Irp->IoStatus.Information = sizeof (PARTITION_INFORMATION); - } - break; - - case IOCTL_DISK_GET_LENGTH_INFO: - if (!ValidateIOBufferSize (Irp, sizeof (GET_LENGTH_INFORMATION), ValidateOutput)) - { - Irp->IoStatus.Status = STATUS_BUFFER_OVERFLOW; - Irp->IoStatus.Information = sizeof (GET_LENGTH_INFORMATION); - } - else - { - PGET_LENGTH_INFORMATION outputBuffer = (PGET_LENGTH_INFORMATION) Irp->AssociatedIrp.SystemBuffer; - - outputBuffer->Length.QuadPart = Extension->DiskLength; - Irp->IoStatus.Status = STATUS_SUCCESS; - Irp->IoStatus.Information = sizeof (GET_LENGTH_INFORMATION); - } - break; - - case IOCTL_DISK_VERIFY: - if (ValidateIOBufferSize (Irp, sizeof (VERIFY_INFORMATION), ValidateInput)) - { - HRESULT hResult; - ULONGLONG ullStartingOffset, ullNewOffset, ullEndOffset; - PVERIFY_INFORMATION pVerifyInformation; - pVerifyInformation = (PVERIFY_INFORMATION) Irp->AssociatedIrp.SystemBuffer; - - ullStartingOffset = (ULONGLONG) pVerifyInformation->StartingOffset.QuadPart; - hResult = ULongLongAdd(ullStartingOffset, - (ULONGLONG) Extension->cryptoInfo->hiddenVolume ? Extension->cryptoInfo->hiddenVolumeOffset : Extension->cryptoInfo->volDataAreaOffset, - &ullNewOffset); - if (hResult != S_OK) - Irp->IoStatus.Status = STATUS_INVALID_PARAMETER; - else if (S_OK != ULongLongAdd(ullStartingOffset, (ULONGLONG) pVerifyInformation->Length, &ullEndOffset)) - Irp->IoStatus.Status = STATUS_INVALID_PARAMETER; - else if (ullEndOffset > (ULONGLONG) Extension->DiskLength) - Irp->IoStatus.Status = STATUS_INVALID_PARAMETER; - else - { - IO_STATUS_BLOCK ioStatus; - PVOID buffer = TCalloc (max (pVerifyInformation->Length, PAGE_SIZE)); - - if (!buffer) - { - Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES; - } - else - { - LARGE_INTEGER offset = pVerifyInformation->StartingOffset; - offset.QuadPart = ullNewOffset; - - Irp->IoStatus.Status = ZwReadFile (Extension->hDeviceFile, NULL, NULL, NULL, &ioStatus, buffer, pVerifyInformation->Length, &offset, NULL); - TCfree (buffer); - - if (NT_SUCCESS (Irp->IoStatus.Status) && ioStatus.Information != pVerifyInformation->Length) - Irp->IoStatus.Status = STATUS_INVALID_PARAMETER; - } - } - - Irp->IoStatus.Information = 0; - } - break; - - case IOCTL_DISK_CHECK_VERIFY: - case IOCTL_STORAGE_CHECK_VERIFY: - { - Irp->IoStatus.Status = STATUS_SUCCESS; - Irp->IoStatus.Information = 0; - - if (irpSp->Parameters.DeviceIoControl.OutputBufferLength >= sizeof (ULONG)) - { - *((ULONG *) Irp->AssociatedIrp.SystemBuffer) = 0; - Irp->IoStatus.Information = sizeof (ULONG); - } - } - break; - - case IOCTL_DISK_IS_WRITABLE: - { - if (Extension->bReadOnly) - Irp->IoStatus.Status = STATUS_MEDIA_WRITE_PROTECTED; - else - Irp->IoStatus.Status = STATUS_SUCCESS; - Irp->IoStatus.Information = 0; - - } - break; - - case IOCTL_VOLUME_ONLINE: - Irp->IoStatus.Status = STATUS_SUCCESS; - Irp->IoStatus.Information = 0; - break; - - case IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS: - - // Vista's filesystem defragmenter fails if IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS does not succeed. - if (!(OsMajorVersion == 6 && OsMinorVersion == 0)) - { - Irp->IoStatus.Status = STATUS_INVALID_DEVICE_REQUEST; - Irp->IoStatus.Information = 0; - } - else if (ValidateIOBufferSize (Irp, sizeof (VOLUME_DISK_EXTENTS), ValidateOutput)) - { - VOLUME_DISK_EXTENTS *extents = (VOLUME_DISK_EXTENTS *) Irp->AssociatedIrp.SystemBuffer; - - // No extent data can be returned as this is not a physical drive. - memset (extents, 0, sizeof (*extents)); - extents->NumberOfDiskExtents = 0; - - Irp->IoStatus.Status = STATUS_SUCCESS; - Irp->IoStatus.Information = sizeof (*extents); - } - break; - - default: - return TCCompleteIrp (Irp, STATUS_INVALID_DEVICE_REQUEST, 0); - } - -#ifdef DEBUG - if (!NT_SUCCESS (Irp->IoStatus.Status)) - { - Dump ("IOCTL error 0x%08x (0x%x %d)\n", - Irp->IoStatus.Status, - (int) (irpSp->Parameters.DeviceIoControl.IoControlCode >> 16), - (int) ((irpSp->Parameters.DeviceIoControl.IoControlCode & 0x1FFF) >> 2)); - } -#endif - - return TCCompleteDiskIrp (Irp, Irp->IoStatus.Status, Irp->IoStatus.Information); -} - - -NTSTATUS ProcessMainDeviceControlIrp (PDEVICE_OBJECT DeviceObject, PEXTENSION Extension, PIRP Irp) -{ - PIO_STACK_LOCATION irpSp = IoGetCurrentIrpStackLocation (Irp); - NTSTATUS ntStatus; - - switch (irpSp->Parameters.DeviceIoControl.IoControlCode) - { - case TC_IOCTL_GET_DRIVER_VERSION: - case TC_IOCTL_LEGACY_GET_DRIVER_VERSION: - if (ValidateIOBufferSize (Irp, sizeof (LONG), ValidateOutput)) - { - LONG tmp = VERSION_NUM; - memcpy (Irp->AssociatedIrp.SystemBuffer, &tmp, 4); - Irp->IoStatus.Information = sizeof (LONG); - Irp->IoStatus.Status = STATUS_SUCCESS; - } - break; - - case TC_IOCTL_GET_DEVICE_REFCOUNT: - if (ValidateIOBufferSize (Irp, sizeof (int), ValidateOutput)) - { - *(int *) Irp->AssociatedIrp.SystemBuffer = DeviceObject->ReferenceCount; - Irp->IoStatus.Information = sizeof (int); - Irp->IoStatus.Status = STATUS_SUCCESS; - } - break; - - case TC_IOCTL_IS_DRIVER_UNLOAD_DISABLED: - if (ValidateIOBufferSize (Irp, sizeof (int), ValidateOutput)) - { - LONG deviceObjectCount = 0; - - *(int *) Irp->AssociatedIrp.SystemBuffer = DriverUnloadDisabled; - - if (IoEnumerateDeviceObjectList (TCDriverObject, NULL, 0, &deviceObjectCount) == STATUS_BUFFER_TOO_SMALL && deviceObjectCount > 1) - *(int *) Irp->AssociatedIrp.SystemBuffer = TRUE; - - Irp->IoStatus.Information = sizeof (int); - Irp->IoStatus.Status = STATUS_SUCCESS; - } - break; - - case TC_IOCTL_IS_ANY_VOLUME_MOUNTED: - if (ValidateIOBufferSize (Irp, sizeof (int), ValidateOutput)) - { - int drive; - *(int *) Irp->AssociatedIrp.SystemBuffer = 0; - - for (drive = MIN_MOUNTED_VOLUME_DRIVE_NUMBER; drive <= MAX_MOUNTED_VOLUME_DRIVE_NUMBER; ++drive) - { - if (GetVirtualVolumeDeviceObject (drive)) - { - *(int *) Irp->AssociatedIrp.SystemBuffer = 1; - break; - } - } - - if (IsBootDriveMounted()) - *(int *) Irp->AssociatedIrp.SystemBuffer = 1; - - Irp->IoStatus.Information = sizeof (int); - Irp->IoStatus.Status = STATUS_SUCCESS; - } - break; - - case TC_IOCTL_OPEN_TEST: - { - OPEN_TEST_STRUCT *opentest = (OPEN_TEST_STRUCT *) Irp->AssociatedIrp.SystemBuffer; - OBJECT_ATTRIBUTES ObjectAttributes; - HANDLE NtFileHandle; - UNICODE_STRING FullFileName; - IO_STATUS_BLOCK IoStatus; - LARGE_INTEGER offset; - ACCESS_MASK access = FILE_READ_ATTRIBUTES; - - if (!ValidateIOBufferSize (Irp, sizeof (OPEN_TEST_STRUCT), ValidateInputOutput)) - break; - - EnsureNullTerminatedString (opentest->wszFileName, sizeof (opentest->wszFileName)); - RtlInitUnicodeString (&FullFileName, opentest->wszFileName); - - InitializeObjectAttributes (&ObjectAttributes, &FullFileName, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, NULL, NULL); - - if (opentest->bDetectTCBootLoader || opentest->DetectFilesystem || opentest->bMatchVolumeID) - access |= FILE_READ_DATA; - - ntStatus = ZwCreateFile (&NtFileHandle, - SYNCHRONIZE | access, &ObjectAttributes, &IoStatus, NULL, - 0, FILE_SHARE_READ | FILE_SHARE_WRITE, FILE_OPEN, FILE_SYNCHRONOUS_IO_NONALERT, NULL, 0); - - if (NT_SUCCESS (ntStatus)) - { - opentest->TCBootLoaderDetected = FALSE; - opentest->FilesystemDetected = FALSE; - opentest->VolumeIDMatched = FALSE; - - if (opentest->bDetectTCBootLoader || opentest->DetectFilesystem || opentest->bMatchVolumeID) - { - byte *readBuffer = TCalloc (TC_MAX_VOLUME_SECTOR_SIZE); - if (!readBuffer) - { - ntStatus = STATUS_INSUFFICIENT_RESOURCES; - } - else - { - if (opentest->bDetectTCBootLoader || opentest->DetectFilesystem) - { - // Determine if the first sector contains a portion of the VeraCrypt Boot Loader - - offset.QuadPart = 0; - - ntStatus = ZwReadFile (NtFileHandle, - NULL, - NULL, - NULL, - &IoStatus, - readBuffer, - TC_MAX_VOLUME_SECTOR_SIZE, - &offset, - NULL); - - if (NT_SUCCESS (ntStatus)) - { - size_t i; - - if (opentest->bDetectTCBootLoader && IoStatus.Information >= TC_SECTOR_SIZE_BIOS) - { - // Search for the string "VeraCrypt" - for (i = 0; i < TC_SECTOR_SIZE_BIOS - strlen (TC_APP_NAME); ++i) - { - if (memcmp (readBuffer + i, TC_APP_NAME, strlen (TC_APP_NAME)) == 0) - { - opentest->TCBootLoaderDetected = TRUE; - break; - } - } - } - - if (opentest->DetectFilesystem && IoStatus.Information >= sizeof (int64)) - { - switch (BE64 (*(uint64 *) readBuffer)) - { - case 0xEB52904E54465320: // NTFS - case 0xEB3C904D53444F53: // FAT16 - case 0xEB58904D53444F53: // FAT32 - case 0xEB76904558464154: // exFAT - - opentest->FilesystemDetected = TRUE; - break; - } - } - } - } - - if (opentest->bMatchVolumeID) - { - int volumeType; - BYTE volumeID[VOLUME_ID_SIZE]; - - // Go through all volume types (e.g., normal, hidden) - for (volumeType = TC_VOLUME_TYPE_NORMAL; - volumeType < TC_VOLUME_TYPE_COUNT; - volumeType++) - { - /* Read the volume header */ - switch (volumeType) - { - case TC_VOLUME_TYPE_NORMAL: - offset.QuadPart = TC_VOLUME_HEADER_OFFSET; - break; - - case TC_VOLUME_TYPE_HIDDEN: - - offset.QuadPart = TC_HIDDEN_VOLUME_HEADER_OFFSET; - break; - } - - ntStatus = ZwReadFile (NtFileHandle, - NULL, - NULL, - NULL, - &IoStatus, - readBuffer, - TC_MAX_VOLUME_SECTOR_SIZE, - &offset, - NULL); - - if (NT_SUCCESS (ntStatus)) - { - /* compute the ID of this volume: SHA-256 of the effective header */ - sha256 (volumeID, readBuffer, TC_VOLUME_HEADER_EFFECTIVE_SIZE); - - if (0 == memcmp (volumeID, opentest->volumeID, VOLUME_ID_SIZE)) - { - opentest->VolumeIDMatched = TRUE; - break; - } - } - } - } - - TCfree (readBuffer); - } - } - - ZwClose (NtFileHandle); - Dump ("Open test on file %ls success.\n", opentest->wszFileName); - } - else - { -#if 0 - Dump ("Open test on file %ls failed NTSTATUS 0x%08x\n", opentest->wszFileName, ntStatus); -#endif - } - - Irp->IoStatus.Information = NT_SUCCESS (ntStatus) ? sizeof (OPEN_TEST_STRUCT) : 0; - Irp->IoStatus.Status = ntStatus; - } - break; - - case TC_IOCTL_GET_SYSTEM_DRIVE_CONFIG: - { - GetSystemDriveConfigurationRequest *request = (GetSystemDriveConfigurationRequest *) Irp->AssociatedIrp.SystemBuffer; - OBJECT_ATTRIBUTES ObjectAttributes; - HANDLE NtFileHandle; - UNICODE_STRING FullFileName; - IO_STATUS_BLOCK IoStatus; - LARGE_INTEGER offset; - byte readBuffer [TC_SECTOR_SIZE_BIOS]; - - if (!ValidateIOBufferSize (Irp, sizeof (GetSystemDriveConfigurationRequest), ValidateInputOutput)) - break; - - EnsureNullTerminatedString (request->DevicePath, sizeof (request->DevicePath)); - RtlInitUnicodeString (&FullFileName, request->DevicePath); - - InitializeObjectAttributes (&ObjectAttributes, &FullFileName, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, NULL, NULL); - - ntStatus = ZwCreateFile (&NtFileHandle, - SYNCHRONIZE | GENERIC_READ, &ObjectAttributes, &IoStatus, NULL, - FILE_ATTRIBUTE_NORMAL, FILE_SHARE_READ | FILE_SHARE_WRITE, FILE_OPEN, FILE_SYNCHRONOUS_IO_NONALERT | FILE_RANDOM_ACCESS, NULL, 0); - - if (NT_SUCCESS (ntStatus)) - { - // Determine if the first sector contains a portion of the VeraCrypt Boot Loader - offset.QuadPart = 0; // MBR - - ntStatus = ZwReadFile (NtFileHandle, - NULL, - NULL, - NULL, - &IoStatus, - readBuffer, - sizeof(readBuffer), - &offset, - NULL); - - if (NT_SUCCESS (ntStatus)) - { - size_t i; - - // Check for dynamic drive - request->DriveIsDynamic = FALSE; - - if (readBuffer[510] == 0x55 && readBuffer[511] == 0xaa) - { - int i; - for (i = 0; i < 4; ++i) - { - if (readBuffer[446 + i * 16 + 4] == PARTITION_LDM) - { - request->DriveIsDynamic = TRUE; - break; - } - } - } - - request->BootLoaderVersion = 0; - request->Configuration = 0; - request->UserConfiguration = 0; - request->CustomUserMessage[0] = 0; - - // Search for the string "VeraCrypt" - for (i = 0; i < sizeof (readBuffer) - strlen (TC_APP_NAME); ++i) - { - if (memcmp (readBuffer + i, TC_APP_NAME, strlen (TC_APP_NAME)) == 0) - { - request->BootLoaderVersion = BE16 (*(uint16 *) (readBuffer + TC_BOOT_SECTOR_VERSION_OFFSET)); - request->Configuration = readBuffer[TC_BOOT_SECTOR_CONFIG_OFFSET]; - - if (request->BootLoaderVersion != 0 && request->BootLoaderVersion <= VERSION_NUM) - { - request->UserConfiguration = readBuffer[TC_BOOT_SECTOR_USER_CONFIG_OFFSET]; - memcpy (request->CustomUserMessage, readBuffer + TC_BOOT_SECTOR_USER_MESSAGE_OFFSET, TC_BOOT_SECTOR_USER_MESSAGE_MAX_LENGTH); - } - break; - } - } - - Irp->IoStatus.Status = STATUS_SUCCESS; - Irp->IoStatus.Information = sizeof (*request); - } - else - { - Irp->IoStatus.Status = ntStatus; - Irp->IoStatus.Information = 0; - } - - ZwClose (NtFileHandle); - - } - else - { - Irp->IoStatus.Status = ntStatus; - Irp->IoStatus.Information = 0; - } - } - break; - - case TC_IOCTL_WIPE_PASSWORD_CACHE: - WipeCache (); - - Irp->IoStatus.Status = STATUS_SUCCESS; - Irp->IoStatus.Information = 0; - break; - - case TC_IOCTL_GET_PASSWORD_CACHE_STATUS: - Irp->IoStatus.Status = cacheEmpty ? STATUS_PIPE_EMPTY : STATUS_SUCCESS; - Irp->IoStatus.Information = 0; - break; - - case TC_IOCTL_SET_PORTABLE_MODE_STATUS: - if (!UserCanAccessDriveDevice()) - { - Irp->IoStatus.Status = STATUS_ACCESS_DENIED; - Irp->IoStatus.Information = 0; - } - else - { - PortableMode = TRUE; - Dump ("Setting portable mode\n"); - } - break; - - case TC_IOCTL_GET_PORTABLE_MODE_STATUS: - Irp->IoStatus.Status = PortableMode ? STATUS_SUCCESS : STATUS_PIPE_EMPTY; - Irp->IoStatus.Information = 0; - break; - - case TC_IOCTL_GET_MOUNTED_VOLUMES: - - if (ValidateIOBufferSize (Irp, sizeof (MOUNT_LIST_STRUCT), ValidateOutput)) - { - MOUNT_LIST_STRUCT *list = (MOUNT_LIST_STRUCT *) Irp->AssociatedIrp.SystemBuffer; - PDEVICE_OBJECT ListDevice; - int drive; - - list->ulMountedDrives = 0; - - for (drive = MIN_MOUNTED_VOLUME_DRIVE_NUMBER; drive <= MAX_MOUNTED_VOLUME_DRIVE_NUMBER; ++drive) - { - PEXTENSION ListExtension; - - ListDevice = GetVirtualVolumeDeviceObject (drive); - if (!ListDevice) - continue; - - ListExtension = (PEXTENSION) ListDevice->DeviceExtension; - if (IsVolumeAccessibleByCurrentUser (ListExtension)) - { - list->ulMountedDrives |= (1 << ListExtension->nDosDriveNo); - RtlStringCbCopyW (list->wszVolume[ListExtension->nDosDriveNo], sizeof(list->wszVolume[ListExtension->nDosDriveNo]),ListExtension->wszVolume); - RtlStringCbCopyW (list->wszLabel[ListExtension->nDosDriveNo], sizeof(list->wszLabel[ListExtension->nDosDriveNo]),ListExtension->wszLabel); - memcpy (list->volumeID[ListExtension->nDosDriveNo], ListExtension->volumeID, VOLUME_ID_SIZE); - list->diskLength[ListExtension->nDosDriveNo] = ListExtension->DiskLength; - list->ea[ListExtension->nDosDriveNo] = ListExtension->cryptoInfo->ea; - if (ListExtension->cryptoInfo->hiddenVolume) - list->volumeType[ListExtension->nDosDriveNo] = PROP_VOL_TYPE_HIDDEN; // Hidden volume - else if (ListExtension->cryptoInfo->bHiddenVolProtectionAction) - list->volumeType[ListExtension->nDosDriveNo] = PROP_VOL_TYPE_OUTER_VOL_WRITE_PREVENTED; // Normal/outer volume (hidden volume protected AND write already prevented) - else if (ListExtension->cryptoInfo->bProtectHiddenVolume) - list->volumeType[ListExtension->nDosDriveNo] = PROP_VOL_TYPE_OUTER; // Normal/outer volume (hidden volume protected) - else - list->volumeType[ListExtension->nDosDriveNo] = PROP_VOL_TYPE_NORMAL; // Normal volume - list->truecryptMode[ListExtension->nDosDriveNo] = ListExtension->cryptoInfo->bTrueCryptMode; - } - } - - Irp->IoStatus.Status = STATUS_SUCCESS; - Irp->IoStatus.Information = sizeof (MOUNT_LIST_STRUCT); - } - break; - - case TC_IOCTL_LEGACY_GET_MOUNTED_VOLUMES: - if (ValidateIOBufferSize (Irp, sizeof (uint32), ValidateOutput)) - { - // Prevent the user from downgrading to versions lower than 5.0 by faking mounted volumes. - // The user could render the system unbootable by downgrading when boot encryption - // is active or being set up. - - memset (Irp->AssociatedIrp.SystemBuffer, 0, irpSp->Parameters.DeviceIoControl.OutputBufferLength); - *(uint32 *) Irp->AssociatedIrp.SystemBuffer = 0xffffFFFF; - - Irp->IoStatus.Status = STATUS_SUCCESS; - Irp->IoStatus.Information = irpSp->Parameters.DeviceIoControl.OutputBufferLength; - } - break; - - case TC_IOCTL_GET_VOLUME_PROPERTIES: - if (ValidateIOBufferSize (Irp, sizeof (VOLUME_PROPERTIES_STRUCT), ValidateInputOutput)) - { - VOLUME_PROPERTIES_STRUCT *prop = (VOLUME_PROPERTIES_STRUCT *) Irp->AssociatedIrp.SystemBuffer; - PDEVICE_OBJECT ListDevice = GetVirtualVolumeDeviceObject (prop->driveNo); - - Irp->IoStatus.Status = STATUS_INVALID_PARAMETER; - Irp->IoStatus.Information = 0; - - if (ListDevice) - { - PEXTENSION ListExtension = (PEXTENSION) ListDevice->DeviceExtension; - if (IsVolumeAccessibleByCurrentUser (ListExtension)) - { - prop->uniqueId = ListExtension->UniqueVolumeId; - RtlStringCbCopyW (prop->wszVolume, sizeof(prop->wszVolume),ListExtension->wszVolume); - RtlStringCbCopyW (prop->wszLabel, sizeof(prop->wszLabel),ListExtension->wszLabel); - memcpy (prop->volumeID, ListExtension->volumeID, VOLUME_ID_SIZE); - prop->bDriverSetLabel = ListExtension->bDriverSetLabel; - prop->diskLength = ListExtension->DiskLength; - prop->ea = ListExtension->cryptoInfo->ea; - prop->mode = ListExtension->cryptoInfo->mode; - prop->pkcs5 = ListExtension->cryptoInfo->pkcs5; - prop->pkcs5Iterations = ListExtension->cryptoInfo->noIterations; - prop->volumePim = ListExtension->cryptoInfo->volumePim; -#if 0 - prop->volumeCreationTime = ListExtension->cryptoInfo->volume_creation_time; - prop->headerCreationTime = ListExtension->cryptoInfo->header_creation_time; -#endif - prop->volumeHeaderFlags = ListExtension->cryptoInfo->HeaderFlags; - prop->readOnly = ListExtension->bReadOnly; - prop->removable = ListExtension->bRemovable; - prop->partitionInInactiveSysEncScope = ListExtension->PartitionInInactiveSysEncScope; - prop->hiddenVolume = ListExtension->cryptoInfo->hiddenVolume; - - if (ListExtension->cryptoInfo->bProtectHiddenVolume) - prop->hiddenVolProtection = ListExtension->cryptoInfo->bHiddenVolProtectionAction ? HIDVOL_PROT_STATUS_ACTION_TAKEN : HIDVOL_PROT_STATUS_ACTIVE; - else - prop->hiddenVolProtection = HIDVOL_PROT_STATUS_NONE; - - prop->totalBytesRead = ListExtension->Queue.TotalBytesRead; - prop->totalBytesWritten = ListExtension->Queue.TotalBytesWritten; - - prop->volFormatVersion = ListExtension->cryptoInfo->LegacyVolume ? TC_VOLUME_FORMAT_VERSION_PRE_6_0 : TC_VOLUME_FORMAT_VERSION; - - Irp->IoStatus.Status = STATUS_SUCCESS; - Irp->IoStatus.Information = sizeof (VOLUME_PROPERTIES_STRUCT); - } - } - } - break; - - case TC_IOCTL_GET_RESOLVED_SYMLINK: - if (ValidateIOBufferSize (Irp, sizeof (RESOLVE_SYMLINK_STRUCT), ValidateInputOutput)) - { - RESOLVE_SYMLINK_STRUCT *resolve = (RESOLVE_SYMLINK_STRUCT *) Irp->AssociatedIrp.SystemBuffer; - { - NTSTATUS ntStatus; - - EnsureNullTerminatedString (resolve->symLinkName, sizeof (resolve->symLinkName)); - - ntStatus = SymbolicLinkToTarget (resolve->symLinkName, - resolve->targetName, - sizeof (resolve->targetName)); - - Irp->IoStatus.Information = sizeof (RESOLVE_SYMLINK_STRUCT); - Irp->IoStatus.Status = ntStatus; - } - } - break; - - case TC_IOCTL_GET_DRIVE_PARTITION_INFO: - if (ValidateIOBufferSize (Irp, sizeof (DISK_PARTITION_INFO_STRUCT), ValidateInputOutput)) - { - DISK_PARTITION_INFO_STRUCT *info = (DISK_PARTITION_INFO_STRUCT *) Irp->AssociatedIrp.SystemBuffer; - { - PARTITION_INFORMATION_EX pi; - NTSTATUS ntStatus; - - EnsureNullTerminatedString (info->deviceName, sizeof (info->deviceName)); - - ntStatus = TCDeviceIoControl (info->deviceName, IOCTL_DISK_GET_PARTITION_INFO_EX, NULL, 0, &pi, sizeof (pi)); - if (NT_SUCCESS(ntStatus)) - { - memset (&info->partInfo, 0, sizeof (info->partInfo)); - - info->partInfo.PartitionLength = pi.PartitionLength; - info->partInfo.PartitionNumber = pi.PartitionNumber; - info->partInfo.StartingOffset = pi.StartingOffset; - - if (pi.PartitionStyle == PARTITION_STYLE_MBR) - { - info->partInfo.PartitionType = pi.Mbr.PartitionType; - info->partInfo.BootIndicator = pi.Mbr.BootIndicator; - } - - info->IsGPT = pi.PartitionStyle == PARTITION_STYLE_GPT; - } - else - { - // Windows 2000 does not support IOCTL_DISK_GET_PARTITION_INFO_EX - ntStatus = TCDeviceIoControl (info->deviceName, IOCTL_DISK_GET_PARTITION_INFO, NULL, 0, &info->partInfo, sizeof (info->partInfo)); - info->IsGPT = FALSE; - } - - if (!NT_SUCCESS (ntStatus)) - { - GET_LENGTH_INFORMATION lengthInfo; - ntStatus = TCDeviceIoControl (info->deviceName, IOCTL_DISK_GET_LENGTH_INFO, NULL, 0, &lengthInfo, sizeof (lengthInfo)); - - if (NT_SUCCESS (ntStatus)) - { - memset (&info->partInfo, 0, sizeof (info->partInfo)); - info->partInfo.PartitionLength = lengthInfo.Length; - } - } - - info->IsDynamic = FALSE; - - if (NT_SUCCESS (ntStatus) && OsMajorVersion >= 6) - { -# define IOCTL_VOLUME_IS_DYNAMIC CTL_CODE(IOCTL_VOLUME_BASE, 18, METHOD_BUFFERED, FILE_ANY_ACCESS) - if (!NT_SUCCESS (TCDeviceIoControl (info->deviceName, IOCTL_VOLUME_IS_DYNAMIC, NULL, 0, &info->IsDynamic, sizeof (info->IsDynamic)))) - info->IsDynamic = FALSE; - } - - Irp->IoStatus.Information = sizeof (DISK_PARTITION_INFO_STRUCT); - Irp->IoStatus.Status = ntStatus; - } - } - break; - - case TC_IOCTL_GET_DRIVE_GEOMETRY: - if (ValidateIOBufferSize (Irp, sizeof (DISK_GEOMETRY_STRUCT), ValidateInputOutput)) - { - DISK_GEOMETRY_STRUCT *g = (DISK_GEOMETRY_STRUCT *) Irp->AssociatedIrp.SystemBuffer; - { - NTSTATUS ntStatus; - - EnsureNullTerminatedString (g->deviceName, sizeof (g->deviceName)); - - ntStatus = TCDeviceIoControl (g->deviceName, - IOCTL_DISK_GET_DRIVE_GEOMETRY, - NULL, 0, &g->diskGeometry, sizeof (g->diskGeometry)); - - Irp->IoStatus.Information = sizeof (DISK_GEOMETRY_STRUCT); - Irp->IoStatus.Status = ntStatus; - } - } - break; - - case TC_IOCTL_PROBE_REAL_DRIVE_SIZE: - if (ValidateIOBufferSize (Irp, sizeof (ProbeRealDriveSizeRequest), ValidateInputOutput)) - { - ProbeRealDriveSizeRequest *request = (ProbeRealDriveSizeRequest *) Irp->AssociatedIrp.SystemBuffer; - NTSTATUS status; - UNICODE_STRING name; - PFILE_OBJECT fileObject; - PDEVICE_OBJECT deviceObject; - - EnsureNullTerminatedString (request->DeviceName, sizeof (request->DeviceName)); - - RtlInitUnicodeString (&name, request->DeviceName); - status = IoGetDeviceObjectPointer (&name, FILE_READ_ATTRIBUTES, &fileObject, &deviceObject); - if (!NT_SUCCESS (status)) - { - Irp->IoStatus.Information = 0; - Irp->IoStatus.Status = status; - break; - } - - status = ProbeRealDriveSize (deviceObject, &request->RealDriveSize); - ObDereferenceObject (fileObject); - - if (status == STATUS_TIMEOUT) - { - request->TimeOut = TRUE; - Irp->IoStatus.Information = sizeof (ProbeRealDriveSizeRequest); - Irp->IoStatus.Status = STATUS_SUCCESS; - } - else if (!NT_SUCCESS (status)) - { - Irp->IoStatus.Information = 0; - Irp->IoStatus.Status = status; - } - else - { - request->TimeOut = FALSE; - Irp->IoStatus.Information = sizeof (ProbeRealDriveSizeRequest); - Irp->IoStatus.Status = status; - } - } - break; - - case TC_IOCTL_MOUNT_VOLUME: - if (ValidateIOBufferSize (Irp, sizeof (MOUNT_STRUCT), ValidateInputOutput)) - { - MOUNT_STRUCT *mount = (MOUNT_STRUCT *) Irp->AssociatedIrp.SystemBuffer; - - if (mount->VolumePassword.Length > MAX_PASSWORD || mount->ProtectedHidVolPassword.Length > MAX_PASSWORD - || mount->pkcs5_prf < 0 || mount->pkcs5_prf > LAST_PRF_ID - || mount->VolumePim < -1 || mount->VolumePim == INT_MAX - || mount->ProtectedHidVolPkcs5Prf < 0 || mount->ProtectedHidVolPkcs5Prf > LAST_PRF_ID - || (mount->bTrueCryptMode != FALSE && mount->bTrueCryptMode != TRUE) - ) - { - Irp->IoStatus.Status = STATUS_INVALID_PARAMETER; - Irp->IoStatus.Information = 0; - break; - } - - EnsureNullTerminatedString (mount->wszVolume, sizeof (mount->wszVolume)); - EnsureNullTerminatedString (mount->wszLabel, sizeof (mount->wszLabel)); - - Irp->IoStatus.Information = sizeof (MOUNT_STRUCT); - Irp->IoStatus.Status = MountDevice (DeviceObject, mount); - - burn (&mount->VolumePassword, sizeof (mount->VolumePassword)); - burn (&mount->ProtectedHidVolPassword, sizeof (mount->ProtectedHidVolPassword)); - burn (&mount->pkcs5_prf, sizeof (mount->pkcs5_prf)); - burn (&mount->VolumePim, sizeof (mount->VolumePim)); - burn (&mount->bTrueCryptMode, sizeof (mount->bTrueCryptMode)); - burn (&mount->ProtectedHidVolPkcs5Prf, sizeof (mount->ProtectedHidVolPkcs5Prf)); - burn (&mount->ProtectedHidVolPim, sizeof (mount->ProtectedHidVolPim)); - } - break; - - case TC_IOCTL_DISMOUNT_VOLUME: - if (ValidateIOBufferSize (Irp, sizeof (UNMOUNT_STRUCT), ValidateInputOutput)) - { - UNMOUNT_STRUCT *unmount = (UNMOUNT_STRUCT *) Irp->AssociatedIrp.SystemBuffer; - PDEVICE_OBJECT ListDevice = GetVirtualVolumeDeviceObject (unmount->nDosDriveNo); - - unmount->nReturnCode = ERR_DRIVE_NOT_FOUND; - - if (ListDevice) - { - PEXTENSION ListExtension = (PEXTENSION) ListDevice->DeviceExtension; - - if (IsVolumeAccessibleByCurrentUser (ListExtension)) - unmount->nReturnCode = UnmountDevice (unmount, ListDevice, unmount->ignoreOpenFiles); - } - - Irp->IoStatus.Information = sizeof (UNMOUNT_STRUCT); - Irp->IoStatus.Status = STATUS_SUCCESS; - } - break; - - case TC_IOCTL_DISMOUNT_ALL_VOLUMES: - if (ValidateIOBufferSize (Irp, sizeof (UNMOUNT_STRUCT), ValidateInputOutput)) - { - UNMOUNT_STRUCT *unmount = (UNMOUNT_STRUCT *) Irp->AssociatedIrp.SystemBuffer; - - unmount->nReturnCode = UnmountAllDevices (unmount, unmount->ignoreOpenFiles); - - Irp->IoStatus.Information = sizeof (UNMOUNT_STRUCT); - Irp->IoStatus.Status = STATUS_SUCCESS; - } - break; - - case TC_IOCTL_BOOT_ENCRYPTION_SETUP: - Irp->IoStatus.Status = StartBootEncryptionSetup (DeviceObject, Irp, irpSp); - Irp->IoStatus.Information = 0; - break; - - case TC_IOCTL_ABORT_BOOT_ENCRYPTION_SETUP: - Irp->IoStatus.Status = AbortBootEncryptionSetup(); - Irp->IoStatus.Information = 0; - break; - - case TC_IOCTL_GET_BOOT_ENCRYPTION_STATUS: - GetBootEncryptionStatus (Irp, irpSp); - break; - - case TC_IOCTL_GET_BOOT_ENCRYPTION_SETUP_RESULT: - Irp->IoStatus.Information = 0; - Irp->IoStatus.Status = GetSetupResult(); - break; - - case TC_IOCTL_GET_BOOT_DRIVE_VOLUME_PROPERTIES: - GetBootDriveVolumeProperties (Irp, irpSp); - break; - - case TC_IOCTL_GET_BOOT_LOADER_VERSION: - GetBootLoaderVersion (Irp, irpSp); - break; - - case TC_IOCTL_REOPEN_BOOT_VOLUME_HEADER: - ReopenBootVolumeHeader (Irp, irpSp); - break; - - case VC_IOCTL_GET_BOOT_LOADER_FINGERPRINT: - GetBootLoaderFingerprint (Irp, irpSp); - break; - - case TC_IOCTL_GET_BOOT_ENCRYPTION_ALGORITHM_NAME: - GetBootEncryptionAlgorithmName (Irp, irpSp); - break; - - case TC_IOCTL_IS_HIDDEN_SYSTEM_RUNNING: - if (ValidateIOBufferSize (Irp, sizeof (int), ValidateOutput)) - { - *(int *) Irp->AssociatedIrp.SystemBuffer = IsHiddenSystemRunning() ? 1 : 0; - Irp->IoStatus.Information = sizeof (int); - Irp->IoStatus.Status = STATUS_SUCCESS; - } - break; - - case TC_IOCTL_START_DECOY_SYSTEM_WIPE: - Irp->IoStatus.Status = StartDecoySystemWipe (DeviceObject, Irp, irpSp); - Irp->IoStatus.Information = 0; - break; - - case TC_IOCTL_ABORT_DECOY_SYSTEM_WIPE: - Irp->IoStatus.Status = AbortDecoySystemWipe(); - Irp->IoStatus.Information = 0; - break; - - case TC_IOCTL_GET_DECOY_SYSTEM_WIPE_RESULT: - Irp->IoStatus.Status = GetDecoySystemWipeResult(); - Irp->IoStatus.Information = 0; - break; - - case TC_IOCTL_GET_DECOY_SYSTEM_WIPE_STATUS: - GetDecoySystemWipeStatus (Irp, irpSp); - break; - - case TC_IOCTL_WRITE_BOOT_DRIVE_SECTOR: - Irp->IoStatus.Status = WriteBootDriveSector (Irp, irpSp); - Irp->IoStatus.Information = 0; - break; - - case TC_IOCTL_GET_WARNING_FLAGS: - if (ValidateIOBufferSize (Irp, sizeof (GetWarningFlagsRequest), ValidateOutput)) - { - GetWarningFlagsRequest *flags = (GetWarningFlagsRequest *) Irp->AssociatedIrp.SystemBuffer; - - flags->PagingFileCreationPrevented = PagingFileCreationPrevented; - PagingFileCreationPrevented = FALSE; - flags->SystemFavoriteVolumeDirty = SystemFavoriteVolumeDirty; - SystemFavoriteVolumeDirty = FALSE; - - Irp->IoStatus.Information = sizeof (GetWarningFlagsRequest); - Irp->IoStatus.Status = STATUS_SUCCESS; - } - break; - - case TC_IOCTL_SET_SYSTEM_FAVORITE_VOLUME_DIRTY: - if (UserCanAccessDriveDevice()) - { - SystemFavoriteVolumeDirty = TRUE; - Irp->IoStatus.Status = STATUS_SUCCESS; - } - else - Irp->IoStatus.Status = STATUS_ACCESS_DENIED; - - Irp->IoStatus.Information = 0; - break; - - case TC_IOCTL_REREAD_DRIVER_CONFIG: - Irp->IoStatus.Status = ReadRegistryConfigFlags (FALSE); - Irp->IoStatus.Information = 0; - break; - - case TC_IOCTL_GET_SYSTEM_DRIVE_DUMP_CONFIG: - if ( (ValidateIOBufferSize (Irp, sizeof (GetSystemDriveDumpConfigRequest), ValidateOutput)) - && (Irp->RequestorMode == KernelMode) - ) - { - GetSystemDriveDumpConfigRequest *request = (GetSystemDriveDumpConfigRequest *) Irp->AssociatedIrp.SystemBuffer; - - request->BootDriveFilterExtension = GetBootDriveFilterExtension(); - if (IsBootDriveMounted() && request->BootDriveFilterExtension) - { - request->HwEncryptionEnabled = IsHwEncryptionEnabled(); - Irp->IoStatus.Status = STATUS_SUCCESS; - Irp->IoStatus.Information = sizeof (*request); - } - else - { - Irp->IoStatus.Status = STATUS_INVALID_PARAMETER; - Irp->IoStatus.Information = 0; - } - } - break; - - default: - return TCCompleteIrp (Irp, STATUS_INVALID_DEVICE_REQUEST, 0); - } - - -#ifdef DEBUG - if (!NT_SUCCESS (Irp->IoStatus.Status)) - { - switch (irpSp->Parameters.DeviceIoControl.IoControlCode) - { - case TC_IOCTL_GET_MOUNTED_VOLUMES: - case TC_IOCTL_GET_PASSWORD_CACHE_STATUS: - case TC_IOCTL_GET_PORTABLE_MODE_STATUS: - case TC_IOCTL_SET_PORTABLE_MODE_STATUS: - case TC_IOCTL_OPEN_TEST: - case TC_IOCTL_GET_RESOLVED_SYMLINK: - case TC_IOCTL_GET_DRIVE_PARTITION_INFO: - case TC_IOCTL_GET_BOOT_DRIVE_VOLUME_PROPERTIES: - case TC_IOCTL_GET_BOOT_ENCRYPTION_STATUS: - case TC_IOCTL_IS_HIDDEN_SYSTEM_RUNNING: - break; - - default: - Dump ("IOCTL error 0x%08x\n", Irp->IoStatus.Status); - } - } -#endif - - return TCCompleteIrp (Irp, Irp->IoStatus.Status, Irp->IoStatus.Information); -} - - -NTSTATUS TCStartThread (PKSTART_ROUTINE threadProc, PVOID threadArg, PKTHREAD *kThread) -{ - return TCStartThreadInProcess (threadProc, threadArg, kThread, NULL); -} - - -NTSTATUS TCStartThreadInProcess (PKSTART_ROUTINE threadProc, PVOID threadArg, PKTHREAD *kThread, PEPROCESS process) -{ - NTSTATUS status; - HANDLE threadHandle; - HANDLE processHandle = NULL; - OBJECT_ATTRIBUTES threadObjAttributes; - - if (process) - { - status = ObOpenObjectByPointer (process, OBJ_KERNEL_HANDLE, NULL, 0, NULL, KernelMode, &processHandle); - if (!NT_SUCCESS (status)) - return status; - } - - InitializeObjectAttributes (&threadObjAttributes, NULL, OBJ_KERNEL_HANDLE, NULL, NULL); - - status = PsCreateSystemThread (&threadHandle, THREAD_ALL_ACCESS, &threadObjAttributes, processHandle, NULL, threadProc, threadArg); - if (!NT_SUCCESS (status)) - return status; - - status = ObReferenceObjectByHandle (threadHandle, THREAD_ALL_ACCESS, NULL, KernelMode, (PVOID *) kThread, NULL); - if (!NT_SUCCESS (status)) - { - ZwClose (threadHandle); - *kThread = NULL; - return status; - } - - if (processHandle) - ZwClose (processHandle); - - ZwClose (threadHandle); - return STATUS_SUCCESS; -} - - -void TCStopThread (PKTHREAD kThread, PKEVENT wakeUpEvent) -{ - if (wakeUpEvent) - KeSetEvent (wakeUpEvent, 0, FALSE); - - KeWaitForSingleObject (kThread, Executive, KernelMode, FALSE, NULL); - ObDereferenceObject (kThread); -} - - -NTSTATUS TCStartVolumeThread (PDEVICE_OBJECT DeviceObject, PEXTENSION Extension, MOUNT_STRUCT * mount) -{ - PTHREAD_BLOCK pThreadBlock = TCalloc (sizeof (THREAD_BLOCK)); - HANDLE hThread; - NTSTATUS ntStatus; - OBJECT_ATTRIBUTES threadObjAttributes; - SECURITY_QUALITY_OF_SERVICE qos; - - Dump ("Starting thread...\n"); - - if (pThreadBlock == NULL) - { - return STATUS_INSUFFICIENT_RESOURCES; - } - else - { - pThreadBlock->DeviceObject = DeviceObject; - pThreadBlock->mount = mount; - } - - qos.Length = sizeof (qos); - qos.ContextTrackingMode = SECURITY_STATIC_TRACKING; - qos.EffectiveOnly = TRUE; - qos.ImpersonationLevel = SecurityImpersonation; - - ntStatus = SeCreateClientSecurity (PsGetCurrentThread(), &qos, FALSE, &Extension->SecurityClientContext); - if (!NT_SUCCESS (ntStatus)) - goto ret; - - Extension->SecurityClientContextValid = TRUE; - - Extension->bThreadShouldQuit = FALSE; - - InitializeObjectAttributes (&threadObjAttributes, NULL, OBJ_KERNEL_HANDLE, NULL, NULL); - - ntStatus = PsCreateSystemThread (&hThread, - THREAD_ALL_ACCESS, - &threadObjAttributes, - NULL, - NULL, - VolumeThreadProc, - pThreadBlock); - - if (!NT_SUCCESS (ntStatus)) - { - Dump ("PsCreateSystemThread Failed END\n"); - goto ret; - } - - ntStatus = ObReferenceObjectByHandle (hThread, - THREAD_ALL_ACCESS, - NULL, - KernelMode, - &Extension->peThread, - NULL); - - ZwClose (hThread); - - if (!NT_SUCCESS (ntStatus)) - goto ret; - - Dump ("Waiting for thread to initialize...\n"); - - KeWaitForSingleObject (&Extension->keCreateEvent, - Executive, - KernelMode, - FALSE, - NULL); - - Dump ("Waiting completed! Thread returns 0x%08x\n", pThreadBlock->ntCreateStatus); - ntStatus = pThreadBlock->ntCreateStatus; - -ret: - TCfree (pThreadBlock); - return ntStatus; -} - -void TCStopVolumeThread (PDEVICE_OBJECT DeviceObject, PEXTENSION Extension) -{ - NTSTATUS ntStatus; - - UNREFERENCED_PARAMETER (DeviceObject); /* Remove compiler warning */ - - Dump ("Signalling thread to quit...\n"); - - Extension->bThreadShouldQuit = TRUE; - - KeReleaseSemaphore (&Extension->RequestSemaphore, - 0, - 1, - TRUE); - - ntStatus = KeWaitForSingleObject (Extension->peThread, - Executive, - KernelMode, - FALSE, - NULL); - - ASSERT (NT_SUCCESS (ntStatus)); - - ObDereferenceObject (Extension->peThread); - Extension->peThread = NULL; - - Dump ("Thread exited\n"); -} - - -// Suspend current thread for a number of milliseconds -void TCSleep (int milliSeconds) -{ - PKTIMER timer = (PKTIMER) TCalloc (sizeof (KTIMER)); - LARGE_INTEGER duetime; - - if (!timer) - return; - - duetime.QuadPart = (__int64) milliSeconds * -10000; - KeInitializeTimerEx(timer, NotificationTimer); - KeSetTimerEx(timer, duetime, 0, NULL); - - KeWaitForSingleObject (timer, Executive, KernelMode, FALSE, NULL); - - TCfree (timer); -} - -BOOL IsDeviceName(wchar_t wszVolume[TC_MAX_PATH]) -{ - if ( (wszVolume[0] == '\\') - && (wszVolume[1] == 'D' || wszVolume[1] == 'd') - && (wszVolume[2] == 'E' || wszVolume[2] == 'e') - && (wszVolume[3] == 'V' || wszVolume[3] == 'v') - && (wszVolume[4] == 'I' || wszVolume[4] == 'i') - && (wszVolume[5] == 'C' || wszVolume[5] == 'c') - && (wszVolume[6] == 'E' || wszVolume[6] == 'e') - ) - { - return TRUE; - } - else - return FALSE; -} - -/* VolumeThreadProc does all the work of processing IRP's, and dispatching them - to either the ReadWrite function or the DeviceControl function */ -VOID VolumeThreadProc (PVOID Context) -{ - PTHREAD_BLOCK pThreadBlock = (PTHREAD_BLOCK) Context; - PDEVICE_OBJECT DeviceObject = pThreadBlock->DeviceObject; - PEXTENSION Extension = (PEXTENSION) DeviceObject->DeviceExtension; - BOOL bDevice; - - /* Set thread priority to lowest realtime level. */ - KeSetPriorityThread (KeGetCurrentThread (), LOW_REALTIME_PRIORITY); - - Dump ("Mount THREAD OPENING VOLUME BEGIN\n"); - - if ( !IsDeviceName (pThreadBlock->mount->wszVolume)) - { - RtlStringCbCopyW (pThreadBlock->wszMountVolume, sizeof(pThreadBlock->wszMountVolume),WIDE ("\\??\\")); - RtlStringCbCatW (pThreadBlock->wszMountVolume, sizeof(pThreadBlock->wszMountVolume),pThreadBlock->mount->wszVolume); - bDevice = FALSE; - } - else - { - pThreadBlock->wszMountVolume[0] = 0; - RtlStringCbCatW (pThreadBlock->wszMountVolume, sizeof(pThreadBlock->wszMountVolume),pThreadBlock->mount->wszVolume); - bDevice = TRUE; - } - - Dump ("Mount THREAD request for File %ls DriveNumber %d Device = %d\n", - pThreadBlock->wszMountVolume, pThreadBlock->mount->nDosDriveNo, bDevice); - - pThreadBlock->ntCreateStatus = TCOpenVolume (DeviceObject, - Extension, - pThreadBlock->mount, - pThreadBlock->wszMountVolume, - bDevice); - - if (!NT_SUCCESS (pThreadBlock->ntCreateStatus) || pThreadBlock->mount->nReturnCode != 0) - { - KeSetEvent (&Extension->keCreateEvent, 0, FALSE); - PsTerminateSystemThread (STATUS_SUCCESS); - } - - // Start IO queue - Extension->Queue.IsFilterDevice = FALSE; - Extension->Queue.DeviceObject = DeviceObject; - Extension->Queue.CryptoInfo = Extension->cryptoInfo; - Extension->Queue.HostFileHandle = Extension->hDeviceFile; - Extension->Queue.VirtualDeviceLength = Extension->DiskLength; - Extension->Queue.MaxReadAheadOffset.QuadPart = Extension->HostLength; - - if (Extension->SecurityClientContextValid) - Extension->Queue.SecurityClientContext = &Extension->SecurityClientContext; - else - Extension->Queue.SecurityClientContext = NULL; - - pThreadBlock->ntCreateStatus = EncryptedIoQueueStart (&Extension->Queue); - - if (!NT_SUCCESS (pThreadBlock->ntCreateStatus)) - { - TCCloseVolume (DeviceObject, Extension); - - pThreadBlock->mount->nReturnCode = ERR_OS_ERROR; - KeSetEvent (&Extension->keCreateEvent, 0, FALSE); - PsTerminateSystemThread (STATUS_SUCCESS); - } - - KeSetEvent (&Extension->keCreateEvent, 0, FALSE); - /* From this point on pThreadBlock cannot be used as it will have been released! */ - pThreadBlock = NULL; - - for (;;) - { - /* Wait for a request from the dispatch routines. */ - KeWaitForSingleObject ((PVOID) & Extension->RequestSemaphore, Executive, KernelMode, FALSE, NULL); - - for (;;) - { - PIO_STACK_LOCATION irpSp; - PLIST_ENTRY request; - PIRP irp; - - request = ExInterlockedRemoveHeadList (&Extension->ListEntry, &Extension->ListSpinLock); - if (request == NULL) - break; - - irp = CONTAINING_RECORD (request, IRP, Tail.Overlay.ListEntry); - irpSp = IoGetCurrentIrpStackLocation (irp); - - ASSERT (irpSp->MajorFunction == IRP_MJ_DEVICE_CONTROL); - - ProcessVolumeDeviceControlIrp (DeviceObject, Extension, irp); - IoReleaseRemoveLock (&Extension->Queue.RemoveLock, irp); - } - - if (Extension->bThreadShouldQuit) - { - Dump ("Closing volume\n"); - EncryptedIoQueueStop (&Extension->Queue); - - TCCloseVolume (DeviceObject, Extension); - PsTerminateSystemThread (STATUS_SUCCESS); - } - } -} - -void TCGetNTNameFromNumber (LPWSTR ntname, int cbNtName, int nDriveNo) -{ - WCHAR tmp[2] = - {0, 0}; - int j = nDriveNo + (WCHAR) 'A'; - - tmp[0] = (short) j; - RtlStringCbCopyW (ntname, cbNtName,(LPWSTR) NT_MOUNT_PREFIX); - RtlStringCbCatW (ntname, cbNtName, tmp); -} - -void TCGetDosNameFromNumber (LPWSTR dosname,int cbDosName, int nDriveNo, DeviceNamespaceType namespaceType) -{ - WCHAR tmp[3] = - {0, ':', 0}; - int j = nDriveNo + (WCHAR) 'A'; - - tmp[0] = (short) j; - - if (DeviceNamespaceGlobal == namespaceType) - { - RtlStringCbCopyW (dosname, cbDosName, (LPWSTR) DOS_MOUNT_PREFIX_GLOBAL); - } - else - { - RtlStringCbCopyW (dosname, cbDosName, (LPWSTR) DOS_MOUNT_PREFIX_DEFAULT); - } - - RtlStringCbCatW (dosname, cbDosName, tmp); -} - -#ifdef _DEBUG -LPWSTR TCTranslateCode (ULONG ulCode) -{ - switch (ulCode) - { -#define TC_CASE_RET_NAME(CODE) case CODE : return L###CODE - - TC_CASE_RET_NAME (TC_IOCTL_ABORT_BOOT_ENCRYPTION_SETUP); - TC_CASE_RET_NAME (TC_IOCTL_ABORT_DECOY_SYSTEM_WIPE); - TC_CASE_RET_NAME (TC_IOCTL_BOOT_ENCRYPTION_SETUP); - TC_CASE_RET_NAME (TC_IOCTL_DISMOUNT_ALL_VOLUMES); - TC_CASE_RET_NAME (TC_IOCTL_DISMOUNT_VOLUME); - TC_CASE_RET_NAME (TC_IOCTL_GET_BOOT_DRIVE_VOLUME_PROPERTIES); - TC_CASE_RET_NAME (TC_IOCTL_GET_BOOT_ENCRYPTION_ALGORITHM_NAME); - TC_CASE_RET_NAME (TC_IOCTL_GET_BOOT_ENCRYPTION_SETUP_RESULT); - TC_CASE_RET_NAME (TC_IOCTL_GET_BOOT_ENCRYPTION_STATUS); - TC_CASE_RET_NAME (TC_IOCTL_GET_BOOT_LOADER_VERSION); - TC_CASE_RET_NAME (TC_IOCTL_GET_DECOY_SYSTEM_WIPE_RESULT); - TC_CASE_RET_NAME (TC_IOCTL_GET_DECOY_SYSTEM_WIPE_STATUS); - TC_CASE_RET_NAME (TC_IOCTL_GET_DEVICE_REFCOUNT); - TC_CASE_RET_NAME (TC_IOCTL_GET_DRIVE_GEOMETRY); - TC_CASE_RET_NAME (TC_IOCTL_GET_DRIVE_PARTITION_INFO); - TC_CASE_RET_NAME (TC_IOCTL_GET_DRIVER_VERSION); - TC_CASE_RET_NAME (TC_IOCTL_GET_MOUNTED_VOLUMES); - TC_CASE_RET_NAME (TC_IOCTL_GET_PASSWORD_CACHE_STATUS); - TC_CASE_RET_NAME (TC_IOCTL_GET_SYSTEM_DRIVE_CONFIG); - TC_CASE_RET_NAME (TC_IOCTL_GET_PORTABLE_MODE_STATUS); - TC_CASE_RET_NAME (TC_IOCTL_SET_PORTABLE_MODE_STATUS); - TC_CASE_RET_NAME (TC_IOCTL_GET_RESOLVED_SYMLINK); - TC_CASE_RET_NAME (TC_IOCTL_GET_SYSTEM_DRIVE_DUMP_CONFIG); - TC_CASE_RET_NAME (TC_IOCTL_GET_VOLUME_PROPERTIES); - TC_CASE_RET_NAME (TC_IOCTL_GET_WARNING_FLAGS); - TC_CASE_RET_NAME (TC_IOCTL_DISK_IS_WRITABLE); - TC_CASE_RET_NAME (TC_IOCTL_IS_ANY_VOLUME_MOUNTED); - TC_CASE_RET_NAME (TC_IOCTL_IS_DRIVER_UNLOAD_DISABLED); - TC_CASE_RET_NAME (TC_IOCTL_IS_HIDDEN_SYSTEM_RUNNING); - TC_CASE_RET_NAME (TC_IOCTL_MOUNT_VOLUME); - TC_CASE_RET_NAME (TC_IOCTL_OPEN_TEST); - TC_CASE_RET_NAME (TC_IOCTL_PROBE_REAL_DRIVE_SIZE); - TC_CASE_RET_NAME (TC_IOCTL_REOPEN_BOOT_VOLUME_HEADER); - TC_CASE_RET_NAME (TC_IOCTL_REREAD_DRIVER_CONFIG); - TC_CASE_RET_NAME (TC_IOCTL_SET_SYSTEM_FAVORITE_VOLUME_DIRTY); - TC_CASE_RET_NAME (TC_IOCTL_START_DECOY_SYSTEM_WIPE); - TC_CASE_RET_NAME (TC_IOCTL_WIPE_PASSWORD_CACHE); - TC_CASE_RET_NAME (TC_IOCTL_WRITE_BOOT_DRIVE_SECTOR); - - TC_CASE_RET_NAME (IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS); - -#undef TC_CASE_RET_NAME - } - - if (ulCode == IOCTL_DISK_GET_DRIVE_GEOMETRY) - return (LPWSTR) _T ("IOCTL_DISK_GET_DRIVE_GEOMETRY"); - else if (ulCode == IOCTL_DISK_GET_DRIVE_GEOMETRY_EX) - return (LPWSTR) _T ("IOCTL_DISK_GET_DRIVE_GEOMETRY_EX"); - else if (ulCode == IOCTL_MOUNTDEV_QUERY_DEVICE_NAME) - return (LPWSTR) _T ("IOCTL_MOUNTDEV_QUERY_DEVICE_NAME"); - else if (ulCode == IOCTL_MOUNTDEV_QUERY_SUGGESTED_LINK_NAME) - return (LPWSTR) _T ("IOCTL_MOUNTDEV_QUERY_SUGGESTED_LINK_NAME"); - else if (ulCode == IOCTL_MOUNTDEV_QUERY_UNIQUE_ID) - return (LPWSTR) _T ("IOCTL_MOUNTDEV_QUERY_UNIQUE_ID"); - else if (ulCode == IOCTL_VOLUME_ONLINE) - return (LPWSTR) _T ("IOCTL_VOLUME_ONLINE"); - else if (ulCode == IOCTL_MOUNTDEV_LINK_CREATED) - return (LPWSTR) _T ("IOCTL_MOUNTDEV_LINK_CREATED"); - else if (ulCode == IOCTL_MOUNTDEV_LINK_DELETED) - return (LPWSTR) _T ("IOCTL_MOUNTDEV_LINK_DELETED"); - else if (ulCode == IOCTL_MOUNTMGR_QUERY_POINTS) - return (LPWSTR) _T ("IOCTL_MOUNTMGR_QUERY_POINTS"); - else if (ulCode == IOCTL_MOUNTMGR_VOLUME_MOUNT_POINT_CREATED) - return (LPWSTR) _T ("IOCTL_MOUNTMGR_VOLUME_MOUNT_POINT_CREATED"); - else if (ulCode == IOCTL_MOUNTMGR_VOLUME_MOUNT_POINT_DELETED) - return (LPWSTR) _T ("IOCTL_MOUNTMGR_VOLUME_MOUNT_POINT_DELETED"); - else if (ulCode == IOCTL_DISK_GET_LENGTH_INFO) - return (LPWSTR) _T ("IOCTL_DISK_GET_LENGTH_INFO"); - else if (ulCode == IOCTL_STORAGE_GET_DEVICE_NUMBER) - return (LPWSTR) _T ("IOCTL_STORAGE_GET_DEVICE_NUMBER"); - else if (ulCode == IOCTL_DISK_GET_PARTITION_INFO) - return (LPWSTR) _T ("IOCTL_DISK_GET_PARTITION_INFO"); - else if (ulCode == IOCTL_DISK_GET_PARTITION_INFO_EX) - return (LPWSTR) _T ("IOCTL_DISK_GET_PARTITION_INFO_EX"); - else if (ulCode == IOCTL_DISK_SET_PARTITION_INFO) - return (LPWSTR) _T ("IOCTL_DISK_SET_PARTITION_INFO"); - else if (ulCode == IOCTL_DISK_GET_DRIVE_LAYOUT) - return (LPWSTR) _T ("IOCTL_DISK_GET_DRIVE_LAYOUT"); - else if (ulCode == IOCTL_DISK_SET_DRIVE_LAYOUT_EX) - return (LPWSTR) _T ("IOCTL_DISK_SET_DRIVE_LAYOUT_EX"); - else if (ulCode == IOCTL_DISK_VERIFY) - return (LPWSTR) _T ("IOCTL_DISK_VERIFY"); - else if (ulCode == IOCTL_DISK_FORMAT_TRACKS) - return (LPWSTR) _T ("IOCTL_DISK_FORMAT_TRACKS"); - else if (ulCode == IOCTL_DISK_REASSIGN_BLOCKS) - return (LPWSTR) _T ("IOCTL_DISK_REASSIGN_BLOCKS"); - else if (ulCode == IOCTL_DISK_PERFORMANCE) - return (LPWSTR) _T ("IOCTL_DISK_PERFORMANCE"); - else if (ulCode == IOCTL_DISK_IS_WRITABLE) - return (LPWSTR) _T ("IOCTL_DISK_IS_WRITABLE"); - else if (ulCode == IOCTL_DISK_LOGGING) - return (LPWSTR) _T ("IOCTL_DISK_LOGGING"); - else if (ulCode == IOCTL_DISK_FORMAT_TRACKS_EX) - return (LPWSTR) _T ("IOCTL_DISK_FORMAT_TRACKS_EX"); - else if (ulCode == IOCTL_DISK_HISTOGRAM_STRUCTURE) - return (LPWSTR) _T ("IOCTL_DISK_HISTOGRAM_STRUCTURE"); - else if (ulCode == IOCTL_DISK_HISTOGRAM_DATA) - return (LPWSTR) _T ("IOCTL_DISK_HISTOGRAM_DATA"); - else if (ulCode == IOCTL_DISK_HISTOGRAM_RESET) - return (LPWSTR) _T ("IOCTL_DISK_HISTOGRAM_RESET"); - else if (ulCode == IOCTL_DISK_REQUEST_STRUCTURE) - return (LPWSTR) _T ("IOCTL_DISK_REQUEST_STRUCTURE"); - else if (ulCode == IOCTL_DISK_REQUEST_DATA) - return (LPWSTR) _T ("IOCTL_DISK_REQUEST_DATA"); - else if (ulCode == IOCTL_DISK_CONTROLLER_NUMBER) - return (LPWSTR) _T ("IOCTL_DISK_CONTROLLER_NUMBER"); - else if (ulCode == SMART_GET_VERSION) - return (LPWSTR) _T ("SMART_GET_VERSION"); - else if (ulCode == SMART_SEND_DRIVE_COMMAND) - return (LPWSTR) _T ("SMART_SEND_DRIVE_COMMAND"); - else if (ulCode == SMART_RCV_DRIVE_DATA) - return (LPWSTR) _T ("SMART_RCV_DRIVE_DATA"); - else if (ulCode == IOCTL_DISK_INTERNAL_SET_VERIFY) - return (LPWSTR) _T ("IOCTL_DISK_INTERNAL_SET_VERIFY"); - else if (ulCode == IOCTL_DISK_INTERNAL_CLEAR_VERIFY) - return (LPWSTR) _T ("IOCTL_DISK_INTERNAL_CLEAR_VERIFY"); - else if (ulCode == IOCTL_DISK_CHECK_VERIFY) - return (LPWSTR) _T ("IOCTL_DISK_CHECK_VERIFY"); - else if (ulCode == IOCTL_DISK_MEDIA_REMOVAL) - return (LPWSTR) _T ("IOCTL_DISK_MEDIA_REMOVAL"); - else if (ulCode == IOCTL_DISK_EJECT_MEDIA) - return (LPWSTR) _T ("IOCTL_DISK_EJECT_MEDIA"); - else if (ulCode == IOCTL_DISK_LOAD_MEDIA) - return (LPWSTR) _T ("IOCTL_DISK_LOAD_MEDIA"); - else if (ulCode == IOCTL_DISK_RESERVE) - return (LPWSTR) _T ("IOCTL_DISK_RESERVE"); - else if (ulCode == IOCTL_DISK_RELEASE) - return (LPWSTR) _T ("IOCTL_DISK_RELEASE"); - else if (ulCode == IOCTL_DISK_FIND_NEW_DEVICES) - return (LPWSTR) _T ("IOCTL_DISK_FIND_NEW_DEVICES"); - else if (ulCode == IOCTL_DISK_GET_MEDIA_TYPES) - return (LPWSTR) _T ("IOCTL_DISK_GET_MEDIA_TYPES"); - else if (ulCode == IOCTL_STORAGE_SET_HOTPLUG_INFO) - return (LPWSTR) _T ("IOCTL_STORAGE_SET_HOTPLUG_INFO"); - else if (ulCode == IRP_MJ_READ) - return (LPWSTR) _T ("IRP_MJ_READ"); - else if (ulCode == IRP_MJ_WRITE) - return (LPWSTR) _T ("IRP_MJ_WRITE"); - else if (ulCode == IRP_MJ_CREATE) - return (LPWSTR) _T ("IRP_MJ_CREATE"); - else if (ulCode == IRP_MJ_CLOSE) - return (LPWSTR) _T ("IRP_MJ_CLOSE"); - else if (ulCode == IRP_MJ_CLEANUP) - return (LPWSTR) _T ("IRP_MJ_CLEANUP"); - else if (ulCode == IRP_MJ_FLUSH_BUFFERS) - return (LPWSTR) _T ("IRP_MJ_FLUSH_BUFFERS"); - else if (ulCode == IRP_MJ_SHUTDOWN) - return (LPWSTR) _T ("IRP_MJ_SHUTDOWN"); - else if (ulCode == IRP_MJ_DEVICE_CONTROL) - return (LPWSTR) _T ("IRP_MJ_DEVICE_CONTROL"); - else - { - return (LPWSTR) _T ("IOCTL"); - } -} - -#endif - -void TCDeleteDeviceObject (PDEVICE_OBJECT DeviceObject, PEXTENSION Extension) -{ - UNICODE_STRING Win32NameString; - NTSTATUS ntStatus; - - Dump ("TCDeleteDeviceObject BEGIN\n"); - - if (Extension->bRootDevice) - { - RtlInitUnicodeString (&Win32NameString, (LPWSTR) DOS_ROOT_PREFIX); - ntStatus = IoDeleteSymbolicLink (&Win32NameString); - if (!NT_SUCCESS (ntStatus)) - Dump ("IoDeleteSymbolicLink failed ntStatus = 0x%08x\n", ntStatus); - - RootDeviceObject = NULL; - } - else - { - if (Extension->peThread != NULL) - TCStopVolumeThread (DeviceObject, Extension); - - if (Extension->UserSid) - TCfree (Extension->UserSid); - - if (Extension->SecurityClientContextValid) - { - if (OsMajorVersion == 5 && OsMinorVersion == 0) - { - ObDereferenceObject (Extension->SecurityClientContext.ClientToken); - } - else - { - // Windows 2000 does not support PsDereferenceImpersonationToken() used by SeDeleteClientSecurity(). - // TODO: Use only SeDeleteClientSecurity() once support for Windows 2000 is dropped. - - VOID (*PsDereferenceImpersonationTokenD) (PACCESS_TOKEN ImpersonationToken); - UNICODE_STRING name; - RtlInitUnicodeString (&name, L"PsDereferenceImpersonationToken"); - - PsDereferenceImpersonationTokenD = MmGetSystemRoutineAddress (&name); - if (!PsDereferenceImpersonationTokenD) - TC_BUG_CHECK (STATUS_NOT_IMPLEMENTED); - -# define PsDereferencePrimaryToken -# define PsDereferenceImpersonationToken PsDereferenceImpersonationTokenD - - SeDeleteClientSecurity (&Extension->SecurityClientContext); - -# undef PsDereferencePrimaryToken -# undef PsDereferenceImpersonationToken - } - } - - VirtualVolumeDeviceObjects[Extension->nDosDriveNo] = NULL; - } - - IoDeleteDevice (DeviceObject); - - Dump ("TCDeleteDeviceObject END\n"); -} - - -VOID TCUnloadDriver (PDRIVER_OBJECT DriverObject) -{ - Dump ("TCUnloadDriver BEGIN\n"); - - OnShutdownPending(); - - if (IsBootDriveMounted()) - TC_BUG_CHECK (STATUS_INVALID_DEVICE_STATE); - - EncryptionThreadPoolStop(); - TCDeleteDeviceObject (RootDeviceObject, (PEXTENSION) RootDeviceObject->DeviceExtension); - - Dump ("TCUnloadDriver END\n"); -} - - -void OnShutdownPending () -{ - UNMOUNT_STRUCT unmount; - memset (&unmount, 0, sizeof (unmount)); - unmount.ignoreOpenFiles = TRUE; - - while (SendDeviceIoControlRequest (RootDeviceObject, TC_IOCTL_DISMOUNT_ALL_VOLUMES, &unmount, sizeof (unmount), &unmount, sizeof (unmount)) == STATUS_INSUFFICIENT_RESOURCES || unmount.HiddenVolumeProtectionTriggered) - unmount.HiddenVolumeProtectionTriggered = FALSE; - - while (SendDeviceIoControlRequest (RootDeviceObject, TC_IOCTL_WIPE_PASSWORD_CACHE, NULL, 0, NULL, 0) == STATUS_INSUFFICIENT_RESOURCES); -} - - -NTSTATUS TCDeviceIoControl (PWSTR deviceName, ULONG IoControlCode, void *InputBuffer, ULONG InputBufferSize, void *OutputBuffer, ULONG OutputBufferSize) -{ - IO_STATUS_BLOCK ioStatusBlock; - NTSTATUS ntStatus; - PIRP irp; - PFILE_OBJECT fileObject; - PDEVICE_OBJECT deviceObject; - KEVENT event; - UNICODE_STRING name; - - RtlInitUnicodeString(&name, deviceName); - ntStatus = IoGetDeviceObjectPointer (&name, FILE_READ_ATTRIBUTES, &fileObject, &deviceObject); - - if (!NT_SUCCESS (ntStatus)) - return ntStatus; - - KeInitializeEvent(&event, NotificationEvent, FALSE); - - irp = IoBuildDeviceIoControlRequest (IoControlCode, - deviceObject, - InputBuffer, InputBufferSize, - OutputBuffer, OutputBufferSize, - FALSE, - &event, - &ioStatusBlock); - - if (irp == NULL) - { - Dump ("IRP allocation failed\n"); - ntStatus = STATUS_INSUFFICIENT_RESOURCES; - goto ret; - } - - IoGetNextIrpStackLocation (irp)->FileObject = fileObject; - - ntStatus = IoCallDriver (deviceObject, irp); - if (ntStatus == STATUS_PENDING) - { - KeWaitForSingleObject (&event, Executive, KernelMode, FALSE, NULL); - ntStatus = ioStatusBlock.Status; - } - -ret: - ObDereferenceObject (fileObject); - return ntStatus; -} - - -typedef struct -{ - PDEVICE_OBJECT deviceObject; ULONG ioControlCode; void *inputBuffer; int inputBufferSize; void *outputBuffer; int outputBufferSize; - NTSTATUS Status; - KEVENT WorkItemCompletedEvent; -} SendDeviceIoControlRequestWorkItemArgs; - - -static VOID SendDeviceIoControlRequestWorkItemRoutine (PDEVICE_OBJECT rootDeviceObject, SendDeviceIoControlRequestWorkItemArgs *arg) -{ - arg->Status = SendDeviceIoControlRequest (arg->deviceObject, arg->ioControlCode, arg->inputBuffer, arg->inputBufferSize, arg->outputBuffer, arg->outputBufferSize); - KeSetEvent (&arg->WorkItemCompletedEvent, IO_NO_INCREMENT, FALSE); -} - - -NTSTATUS SendDeviceIoControlRequest (PDEVICE_OBJECT deviceObject, ULONG ioControlCode, void *inputBuffer, int inputBufferSize, void *outputBuffer, int outputBufferSize) -{ - IO_STATUS_BLOCK ioStatusBlock; - NTSTATUS status; - PIRP irp; - KEVENT event; - - if (KeGetCurrentIrql() > APC_LEVEL) - { - SendDeviceIoControlRequestWorkItemArgs args; - - PIO_WORKITEM workItem = IoAllocateWorkItem (RootDeviceObject); - if (!workItem) - return STATUS_INSUFFICIENT_RESOURCES; - - args.deviceObject = deviceObject; - args.ioControlCode = ioControlCode; - args.inputBuffer = inputBuffer; - args.inputBufferSize = inputBufferSize; - args.outputBuffer = outputBuffer; - args.outputBufferSize = outputBufferSize; - - KeInitializeEvent (&args.WorkItemCompletedEvent, SynchronizationEvent, FALSE); - IoQueueWorkItem (workItem, SendDeviceIoControlRequestWorkItemRoutine, DelayedWorkQueue, &args); - - KeWaitForSingleObject (&args.WorkItemCompletedEvent, Executive, KernelMode, FALSE, NULL); - IoFreeWorkItem (workItem); - - return args.Status; - } - - KeInitializeEvent (&event, NotificationEvent, FALSE); - - irp = IoBuildDeviceIoControlRequest (ioControlCode, deviceObject, inputBuffer, inputBufferSize, - outputBuffer, outputBufferSize, FALSE, &event, &ioStatusBlock); - - if (!irp) - return STATUS_INSUFFICIENT_RESOURCES; - - ObReferenceObject (deviceObject); - - status = IoCallDriver (deviceObject, irp); - if (status == STATUS_PENDING) - { - KeWaitForSingleObject (&event, Executive, KernelMode, FALSE, NULL); - status = ioStatusBlock.Status; - } - - ObDereferenceObject (deviceObject); - return status; -} - - -NTSTATUS ProbeRealDriveSize (PDEVICE_OBJECT driveDeviceObject, LARGE_INTEGER *driveSize) -{ - NTSTATUS status; - LARGE_INTEGER sysLength; - LARGE_INTEGER offset; - byte *sectorBuffer; - ULONGLONG startTime; - - if (!UserCanAccessDriveDevice()) - return STATUS_ACCESS_DENIED; - - sectorBuffer = TCalloc (TC_SECTOR_SIZE_BIOS); - if (!sectorBuffer) - return STATUS_INSUFFICIENT_RESOURCES; - - status = SendDeviceIoControlRequest (driveDeviceObject, IOCTL_DISK_GET_LENGTH_INFO, - NULL, 0, &sysLength, sizeof (sysLength)); - - if (!NT_SUCCESS (status)) - { - Dump ("Failed to get drive size - error %x\n", status); - TCfree (sectorBuffer); - return status; - } - - startTime = KeQueryInterruptTime (); - for (offset.QuadPart = sysLength.QuadPart; ; offset.QuadPart += TC_SECTOR_SIZE_BIOS) - { - status = TCReadDevice (driveDeviceObject, sectorBuffer, offset, TC_SECTOR_SIZE_BIOS); - - if (NT_SUCCESS (status)) - status = TCWriteDevice (driveDeviceObject, sectorBuffer, offset, TC_SECTOR_SIZE_BIOS); - - if (!NT_SUCCESS (status)) - { - driveSize->QuadPart = offset.QuadPart; - Dump ("Real drive size = %I64d bytes (%I64d hidden)\n", driveSize->QuadPart, driveSize->QuadPart - sysLength.QuadPart); - TCfree (sectorBuffer); - return STATUS_SUCCESS; - } - - if (KeQueryInterruptTime() - startTime > 3ULL * 60 * 1000 * 1000 * 10) - { - // Abort if probing for more than 3 minutes - driveSize->QuadPart = sysLength.QuadPart; - TCfree (sectorBuffer); - return STATUS_TIMEOUT; - } - } -} - - -NTSTATUS TCOpenFsVolume (PEXTENSION Extension, PHANDLE volumeHandle, PFILE_OBJECT * fileObject) -{ - NTSTATUS ntStatus; - OBJECT_ATTRIBUTES objectAttributes; - UNICODE_STRING fullFileName; - IO_STATUS_BLOCK ioStatus; - WCHAR volumeName[TC_MAX_PATH]; - - TCGetNTNameFromNumber (volumeName, sizeof(volumeName),Extension->nDosDriveNo); - RtlInitUnicodeString (&fullFileName, volumeName); - InitializeObjectAttributes (&objectAttributes, &fullFileName, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, NULL, NULL); - - ntStatus = ZwCreateFile (volumeHandle, - SYNCHRONIZE | GENERIC_READ, - &objectAttributes, - &ioStatus, - NULL, - FILE_ATTRIBUTE_NORMAL, - FILE_SHARE_READ | FILE_SHARE_WRITE, - FILE_OPEN, - FILE_SYNCHRONOUS_IO_NONALERT, - NULL, - 0); - - Dump ("Volume %ls open NTSTATUS 0x%08x\n", volumeName, ntStatus); - - if (!NT_SUCCESS (ntStatus)) - return ntStatus; - - ntStatus = ObReferenceObjectByHandle (*volumeHandle, - FILE_READ_DATA, - NULL, - KernelMode, - fileObject, - NULL); - - if (!NT_SUCCESS (ntStatus)) - ZwClose (*volumeHandle); - - return ntStatus; -} - - -void TCCloseFsVolume (HANDLE volumeHandle, PFILE_OBJECT fileObject) -{ - ObDereferenceObject (fileObject); - ZwClose (volumeHandle); -} - - -static NTSTATUS TCReadWriteDevice (BOOL write, PDEVICE_OBJECT deviceObject, PVOID buffer, LARGE_INTEGER offset, ULONG length) -{ - NTSTATUS status; - IO_STATUS_BLOCK ioStatusBlock; - PIRP irp; - KEVENT completionEvent; - - ASSERT (KeGetCurrentIrql() <= APC_LEVEL); - - KeInitializeEvent (&completionEvent, NotificationEvent, FALSE); - irp = IoBuildSynchronousFsdRequest (write ? IRP_MJ_WRITE : IRP_MJ_READ, deviceObject, buffer, length, &offset, &completionEvent, &ioStatusBlock); - if (!irp) - return STATUS_INSUFFICIENT_RESOURCES; - - ObReferenceObject (deviceObject); - status = IoCallDriver (deviceObject, irp); - - if (status == STATUS_PENDING) - { - status = KeWaitForSingleObject (&completionEvent, Executive, KernelMode, FALSE, NULL); - if (NT_SUCCESS (status)) - status = ioStatusBlock.Status; - } - - ObDereferenceObject (deviceObject); - return status; -} - - -NTSTATUS TCReadDevice (PDEVICE_OBJECT deviceObject, PVOID buffer, LARGE_INTEGER offset, ULONG length) -{ - return TCReadWriteDevice (FALSE, deviceObject, buffer, offset, length); -} - - -NTSTATUS TCWriteDevice (PDEVICE_OBJECT deviceObject, PVOID buffer, LARGE_INTEGER offset, ULONG length) -{ - return TCReadWriteDevice (TRUE, deviceObject, buffer, offset, length); -} - - -NTSTATUS TCFsctlCall (PFILE_OBJECT fileObject, LONG IoControlCode, - void *InputBuffer, int InputBufferSize, void *OutputBuffer, int OutputBufferSize) -{ - IO_STATUS_BLOCK ioStatusBlock; - NTSTATUS ntStatus; - PIRP irp; - KEVENT event; - PIO_STACK_LOCATION stack; - PDEVICE_OBJECT deviceObject = IoGetRelatedDeviceObject (fileObject); - - KeInitializeEvent(&event, NotificationEvent, FALSE); - - irp = IoBuildDeviceIoControlRequest (IoControlCode, - deviceObject, - InputBuffer, InputBufferSize, - OutputBuffer, OutputBufferSize, - FALSE, - &event, - &ioStatusBlock); - - if (irp == NULL) - return STATUS_INSUFFICIENT_RESOURCES; - - stack = IoGetNextIrpStackLocation(irp); - - stack->MajorFunction = IRP_MJ_FILE_SYSTEM_CONTROL; - stack->MinorFunction = IRP_MN_USER_FS_REQUEST; - stack->FileObject = fileObject; - - ntStatus = IoCallDriver (deviceObject, irp); - if (ntStatus == STATUS_PENDING) - { - KeWaitForSingleObject (&event, Executive, KernelMode, FALSE, NULL); - ntStatus = ioStatusBlock.Status; - } - - return ntStatus; -} - - -NTSTATUS CreateDriveLink (int nDosDriveNo) -{ - WCHAR dev[128], link[128]; - UNICODE_STRING deviceName, symLink; - NTSTATUS ntStatus; - - TCGetNTNameFromNumber (dev, sizeof(dev),nDosDriveNo); - TCGetDosNameFromNumber (link, sizeof(link),nDosDriveNo, DeviceNamespaceDefault); - - RtlInitUnicodeString (&deviceName, dev); - RtlInitUnicodeString (&symLink, link); - - ntStatus = IoCreateSymbolicLink (&symLink, &deviceName); - Dump ("IoCreateSymbolicLink returned %X\n", ntStatus); - return ntStatus; -} - - -NTSTATUS RemoveDriveLink (int nDosDriveNo) -{ - WCHAR link[256]; - UNICODE_STRING symLink; - NTSTATUS ntStatus; - - TCGetDosNameFromNumber (link, sizeof(link),nDosDriveNo, DeviceNamespaceDefault); - RtlInitUnicodeString (&symLink, link); - - ntStatus = IoDeleteSymbolicLink (&symLink); - Dump ("IoDeleteSymbolicLink returned %X\n", ntStatus); - return ntStatus; -} - - -NTSTATUS MountManagerMount (MOUNT_STRUCT *mount) -{ - NTSTATUS ntStatus; - WCHAR arrVolume[256]; - char buf[200]; - PMOUNTMGR_TARGET_NAME in = (PMOUNTMGR_TARGET_NAME) buf; - PMOUNTMGR_CREATE_POINT_INPUT point = (PMOUNTMGR_CREATE_POINT_INPUT) buf; - - TCGetNTNameFromNumber (arrVolume, sizeof(arrVolume),mount->nDosDriveNo); - in->DeviceNameLength = (USHORT) wcslen (arrVolume) * 2; - RtlStringCbCopyW(in->DeviceName, sizeof(buf) - sizeof(in->DeviceNameLength),arrVolume); - - ntStatus = TCDeviceIoControl (MOUNTMGR_DEVICE_NAME, IOCTL_MOUNTMGR_VOLUME_ARRIVAL_NOTIFICATION, - in, (ULONG) (sizeof (in->DeviceNameLength) + wcslen (arrVolume) * 2), 0, 0); - - memset (buf, 0, sizeof buf); - TCGetDosNameFromNumber ((PWSTR) &point[1], sizeof(buf) - sizeof(MOUNTMGR_CREATE_POINT_INPUT),mount->nDosDriveNo, DeviceNamespaceDefault); - - point->SymbolicLinkNameOffset = sizeof (MOUNTMGR_CREATE_POINT_INPUT); - point->SymbolicLinkNameLength = (USHORT) wcslen ((PWSTR) &point[1]) * 2; - - point->DeviceNameOffset = point->SymbolicLinkNameOffset + point->SymbolicLinkNameLength; - TCGetNTNameFromNumber ((PWSTR) (buf + point->DeviceNameOffset), sizeof(buf) - point->DeviceNameOffset,mount->nDosDriveNo); - point->DeviceNameLength = (USHORT) wcslen ((PWSTR) (buf + point->DeviceNameOffset)) * 2; - - ntStatus = TCDeviceIoControl (MOUNTMGR_DEVICE_NAME, IOCTL_MOUNTMGR_CREATE_POINT, point, - point->DeviceNameOffset + point->DeviceNameLength, 0, 0); - - return ntStatus; -} - - -NTSTATUS MountManagerUnmount (int nDosDriveNo) -{ - NTSTATUS ntStatus; - char buf[256], out[300]; - PMOUNTMGR_MOUNT_POINT in = (PMOUNTMGR_MOUNT_POINT) buf; - - memset (buf, 0, sizeof buf); - - TCGetDosNameFromNumber ((PWSTR) &in[1], sizeof(buf) - sizeof(MOUNTMGR_MOUNT_POINT),nDosDriveNo, DeviceNamespaceDefault); - - // Only symbolic link can be deleted with IOCTL_MOUNTMGR_DELETE_POINTS. If any other entry is specified, the mount manager will ignore subsequent IOCTL_MOUNTMGR_VOLUME_ARRIVAL_NOTIFICATION for the same volume ID. - in->SymbolicLinkNameOffset = sizeof (MOUNTMGR_MOUNT_POINT); - in->SymbolicLinkNameLength = (USHORT) wcslen ((PWCHAR) &in[1]) * 2; - - ntStatus = TCDeviceIoControl (MOUNTMGR_DEVICE_NAME, IOCTL_MOUNTMGR_DELETE_POINTS, - in, sizeof(MOUNTMGR_MOUNT_POINT) + in->SymbolicLinkNameLength, out, sizeof out); - - Dump ("IOCTL_MOUNTMGR_DELETE_POINTS returned 0x%08x\n", ntStatus); - - return ntStatus; -} - - -NTSTATUS MountDevice (PDEVICE_OBJECT DeviceObject, MOUNT_STRUCT *mount) -{ - PDEVICE_OBJECT NewDeviceObject; - NTSTATUS ntStatus; - - // Make sure the user is asking for a reasonable nDosDriveNo - if (mount->nDosDriveNo >= 0 && mount->nDosDriveNo <= 25 - && IsDriveLetterAvailable (mount->nDosDriveNo, DeviceNamespaceDefault) // drive letter must not exist both locally and globally - && IsDriveLetterAvailable (mount->nDosDriveNo, DeviceNamespaceGlobal) - ) - { - Dump ("Mount request looks valid\n"); - } - else - { - Dump ("WARNING: MOUNT DRIVE LETTER INVALID\n"); - mount->nReturnCode = ERR_DRIVE_NOT_FOUND; - return ERR_DRIVE_NOT_FOUND; - } - - if (!SelfTestsPassed) - { - mount->nReturnCode = ERR_SELF_TESTS_FAILED; - return ERR_SELF_TESTS_FAILED; - } - - ntStatus = TCCreateDeviceObject (DeviceObject->DriverObject, &NewDeviceObject, mount); - - if (!NT_SUCCESS (ntStatus)) - { - Dump ("Mount CREATE DEVICE ERROR, ntStatus = 0x%08x\n", ntStatus); - return ntStatus; - } - else - { - PEXTENSION NewExtension = (PEXTENSION) NewDeviceObject->DeviceExtension; - SECURITY_SUBJECT_CONTEXT subContext; - PACCESS_TOKEN accessToken; - - SeCaptureSubjectContext (&subContext); - SeLockSubjectContext(&subContext); - if (subContext.ClientToken && subContext.ImpersonationLevel >= SecurityImpersonation) - accessToken = subContext.ClientToken; - else - accessToken = subContext.PrimaryToken; - - if (!accessToken) - { - ntStatus = STATUS_INVALID_PARAMETER; - } - else - { - PTOKEN_USER tokenUser; - - ntStatus = SeQueryInformationToken (accessToken, TokenUser, &tokenUser); - if (NT_SUCCESS (ntStatus)) - { - ULONG sidLength = RtlLengthSid (tokenUser->User.Sid); - - NewExtension->UserSid = TCalloc (sidLength); - if (!NewExtension->UserSid) - ntStatus = STATUS_INSUFFICIENT_RESOURCES; - else - ntStatus = RtlCopySid (sidLength, NewExtension->UserSid, tokenUser->User.Sid); - - ExFreePool (tokenUser); // Documented in newer versions of WDK - } - } - - SeUnlockSubjectContext(&subContext); - SeReleaseSubjectContext (&subContext); - - if (NT_SUCCESS (ntStatus)) - ntStatus = TCStartVolumeThread (NewDeviceObject, NewExtension, mount); - - if (!NT_SUCCESS (ntStatus)) - { - Dump ("Mount FAILURE NT ERROR, ntStatus = 0x%08x\n", ntStatus); - TCDeleteDeviceObject (NewDeviceObject, NewExtension); - return ntStatus; - } - else - { - if (mount->nReturnCode == 0) - { - HANDLE volumeHandle; - PFILE_OBJECT volumeFileObject; - ULONG labelLen = (ULONG) wcslen (mount->wszLabel); - BOOL bIsNTFS = FALSE; - ULONG labelMaxLen, labelEffectiveLen; - - Dump ("Mount SUCCESS TC code = 0x%08x READ-ONLY = %d\n", mount->nReturnCode, NewExtension->bReadOnly); - - if (NewExtension->bReadOnly) - NewDeviceObject->Characteristics |= FILE_READ_ONLY_DEVICE; - - NewDeviceObject->Flags &= ~DO_DEVICE_INITIALIZING; - - NewExtension->UniqueVolumeId = LastUniqueVolumeId++; - - // check again that the drive letter is available globally and locally - if ( !IsDriveLetterAvailable (mount->nDosDriveNo, DeviceNamespaceDefault) - || !IsDriveLetterAvailable (mount->nDosDriveNo, DeviceNamespaceGlobal) - ) - { - TCDeleteDeviceObject (NewDeviceObject, NewExtension); - mount->nReturnCode = ERR_DRIVE_NOT_FOUND; - return ERR_DRIVE_NOT_FOUND; - } - - if (mount->bMountManager) - MountManagerMount (mount); - - NewExtension->bMountManager = mount->bMountManager; - - // We create symbolic link even if mount manager is notified of - // arriving volume as it apparently sometimes fails to create the link - CreateDriveLink (mount->nDosDriveNo); - - mount->FilesystemDirty = FALSE; - - if (NT_SUCCESS (TCOpenFsVolume (NewExtension, &volumeHandle, &volumeFileObject))) - { - __try - { - ULONG fsStatus; - - if (NT_SUCCESS (TCFsctlCall (volumeFileObject, FSCTL_IS_VOLUME_DIRTY, NULL, 0, &fsStatus, sizeof (fsStatus))) - && (fsStatus & VOLUME_IS_DIRTY)) - { - mount->FilesystemDirty = TRUE; - } - } - __except (EXCEPTION_EXECUTE_HANDLER) - { - mount->FilesystemDirty = TRUE; - } - - // detect if the filesystem is NTFS or FAT - __try - { - NTFS_VOLUME_DATA_BUFFER ntfsData; - if (NT_SUCCESS (TCFsctlCall (volumeFileObject, FSCTL_GET_NTFS_VOLUME_DATA, NULL, 0, &ntfsData, sizeof (ntfsData)))) - { - bIsNTFS = TRUE; - } - } - __except (EXCEPTION_EXECUTE_HANDLER) - { - bIsNTFS = FALSE; - } - - NewExtension->bIsNTFS = bIsNTFS; - mount->bIsNTFS = bIsNTFS; - - if (labelLen > 0) - { - if (bIsNTFS) - labelMaxLen = 32; // NTFS maximum label length - else - labelMaxLen = 11; // FAT maximum label length - - // calculate label effective length - labelEffectiveLen = labelLen > labelMaxLen? labelMaxLen : labelLen; - - // correct the label in the device - memset (&NewExtension->wszLabel[labelEffectiveLen], 0, 33 - labelEffectiveLen); - memcpy (mount->wszLabel, NewExtension->wszLabel, 33); - - // set the volume label - __try - { - IO_STATUS_BLOCK ioblock; - ULONG labelInfoSize = sizeof(FILE_FS_LABEL_INFORMATION) + (labelEffectiveLen * sizeof(WCHAR)); - FILE_FS_LABEL_INFORMATION* labelInfo = (FILE_FS_LABEL_INFORMATION*) TCalloc (labelInfoSize); - labelInfo->VolumeLabelLength = labelEffectiveLen * sizeof(WCHAR); - memcpy (labelInfo->VolumeLabel, mount->wszLabel, labelInfo->VolumeLabelLength); - - if (STATUS_SUCCESS == ZwSetVolumeInformationFile (volumeHandle, &ioblock, labelInfo, labelInfoSize, FileFsLabelInformation)) - { - mount->bDriverSetLabel = TRUE; - NewExtension->bDriverSetLabel = TRUE; - } - - TCfree(labelInfo); - } - __except (EXCEPTION_EXECUTE_HANDLER) - { - - } - } - - TCCloseFsVolume (volumeHandle, volumeFileObject); - } - } - else - { - Dump ("Mount FAILURE TC code = 0x%08x\n", mount->nReturnCode); - TCDeleteDeviceObject (NewDeviceObject, NewExtension); - } - - return STATUS_SUCCESS; - } - } -} - -NTSTATUS UnmountDevice (UNMOUNT_STRUCT *unmountRequest, PDEVICE_OBJECT deviceObject, BOOL ignoreOpenFiles) -{ - PEXTENSION extension = deviceObject->DeviceExtension; - NTSTATUS ntStatus; - HANDLE volumeHandle; - PFILE_OBJECT volumeFileObject; - - Dump ("UnmountDevice %d\n", extension->nDosDriveNo); - - ntStatus = TCOpenFsVolume (extension, &volumeHandle, &volumeFileObject); - - if (NT_SUCCESS (ntStatus)) - { - int dismountRetry; - - // Dismounting a writable NTFS filesystem prevents the driver from being unloaded on Windows 7 - if (IsOSAtLeast (WIN_7) && !extension->bReadOnly) - { - NTFS_VOLUME_DATA_BUFFER ntfsData; - - if (NT_SUCCESS (TCFsctlCall (volumeFileObject, FSCTL_GET_NTFS_VOLUME_DATA, NULL, 0, &ntfsData, sizeof (ntfsData)))) - DriverUnloadDisabled = TRUE; - } - - // Lock volume - ntStatus = TCFsctlCall (volumeFileObject, FSCTL_LOCK_VOLUME, NULL, 0, NULL, 0); - Dump ("FSCTL_LOCK_VOLUME returned %X\n", ntStatus); - - if (!NT_SUCCESS (ntStatus) && !ignoreOpenFiles) - { - TCCloseFsVolume (volumeHandle, volumeFileObject); - return ERR_FILES_OPEN; - } - - // Dismount volume - for (dismountRetry = 0; dismountRetry < 200; ++dismountRetry) - { - ntStatus = TCFsctlCall (volumeFileObject, FSCTL_DISMOUNT_VOLUME, NULL, 0, NULL, 0); - Dump ("FSCTL_DISMOUNT_VOLUME returned %X\n", ntStatus); - - if (NT_SUCCESS (ntStatus) || ntStatus == STATUS_VOLUME_DISMOUNTED) - break; - - if (!ignoreOpenFiles) - { - TCCloseFsVolume (volumeHandle, volumeFileObject); - return ERR_FILES_OPEN; - } - - TCSleep (100); - } - } - else - { - // Volume cannot be opened => force dismount if allowed - if (!ignoreOpenFiles) - return ERR_FILES_OPEN; - else - volumeHandle = NULL; - } - - if (extension->bMountManager) - MountManagerUnmount (extension->nDosDriveNo); - - // We always remove symbolic link as mount manager might fail to do so - RemoveDriveLink (extension->nDosDriveNo); - - extension->bShuttingDown = TRUE; - - ntStatus = IoAcquireRemoveLock (&extension->Queue.RemoveLock, NULL); - ASSERT (NT_SUCCESS (ntStatus)); - IoReleaseRemoveLockAndWait (&extension->Queue.RemoveLock, NULL); - - if (volumeHandle != NULL) - TCCloseFsVolume (volumeHandle, volumeFileObject); - - if (unmountRequest) - { - PCRYPTO_INFO cryptoInfo = ((PEXTENSION) deviceObject->DeviceExtension)->cryptoInfo; - unmountRequest->HiddenVolumeProtectionTriggered = (cryptoInfo->bProtectHiddenVolume && cryptoInfo->bHiddenVolProtectionAction); - } - - TCDeleteDeviceObject (deviceObject, (PEXTENSION) deviceObject->DeviceExtension); - return 0; -} - - -static PDEVICE_OBJECT FindVolumeWithHighestUniqueId (int maxUniqueId) -{ - PDEVICE_OBJECT highestIdDevice = NULL; - int highestId = -1; - int drive; - - for (drive = MIN_MOUNTED_VOLUME_DRIVE_NUMBER; drive <= MAX_MOUNTED_VOLUME_DRIVE_NUMBER; ++drive) - { - PDEVICE_OBJECT device = GetVirtualVolumeDeviceObject (drive); - if (device) - { - PEXTENSION extension = (PEXTENSION) device->DeviceExtension; - if (extension->UniqueVolumeId > highestId && extension->UniqueVolumeId <= maxUniqueId) - { - highestId = extension->UniqueVolumeId; - highestIdDevice = device; - } - } - } - - return highestIdDevice; -} - - -NTSTATUS UnmountAllDevices (UNMOUNT_STRUCT *unmountRequest, BOOL ignoreOpenFiles) -{ - NTSTATUS status = 0; - PDEVICE_OBJECT ListDevice; - int maxUniqueId = LastUniqueVolumeId; - - Dump ("Unmounting all volumes\n"); - - if (unmountRequest) - unmountRequest->HiddenVolumeProtectionTriggered = FALSE; - - // Dismount volumes in the reverse order they were mounted to properly dismount nested volumes - while ((ListDevice = FindVolumeWithHighestUniqueId (maxUniqueId)) != NULL) - { - PEXTENSION ListExtension = (PEXTENSION) ListDevice->DeviceExtension; - maxUniqueId = ListExtension->UniqueVolumeId - 1; - - if (IsVolumeAccessibleByCurrentUser (ListExtension)) - { - NTSTATUS ntStatus; - - if (unmountRequest) - unmountRequest->nDosDriveNo = ListExtension->nDosDriveNo; - - ntStatus = UnmountDevice (unmountRequest, ListDevice, ignoreOpenFiles); - status = ntStatus == 0 ? status : ntStatus; - - if (unmountRequest && unmountRequest->HiddenVolumeProtectionTriggered) - break; - } - } - - return status; -} - -// Resolves symbolic link name to its target name -NTSTATUS SymbolicLinkToTarget (PWSTR symlinkName, PWSTR targetName, USHORT maxTargetNameLength) -{ - NTSTATUS ntStatus; - OBJECT_ATTRIBUTES objectAttributes; - UNICODE_STRING fullFileName; - HANDLE handle; - - RtlInitUnicodeString (&fullFileName, symlinkName); - InitializeObjectAttributes (&objectAttributes, &fullFileName, OBJ_KERNEL_HANDLE | OBJ_CASE_INSENSITIVE, NULL, NULL); - - ntStatus = ZwOpenSymbolicLinkObject (&handle, GENERIC_READ, &objectAttributes); - - if (NT_SUCCESS (ntStatus)) - { - UNICODE_STRING target; - target.Buffer = targetName; - target.Length = 0; - target.MaximumLength = maxTargetNameLength; - memset (targetName, 0, maxTargetNameLength); - - ntStatus = ZwQuerySymbolicLinkObject (handle, &target, NULL); - - ZwClose (handle); - } - - return ntStatus; -} - - -// Checks if two regions overlap (borders are parts of regions) -BOOL RegionsOverlap (unsigned __int64 start1, unsigned __int64 end1, unsigned __int64 start2, unsigned __int64 end2) -{ - return (start1 < start2) ? (end1 >= start2) : (start1 <= end2); -} - - -void GetIntersection (uint64 start1, uint32 length1, uint64 start2, uint64 end2, uint64 *intersectStart, uint32 *intersectLength) -{ - uint64 end1 = start1 + length1 - 1; - uint64 intersectEnd = (end1 <= end2) ? end1 : end2; - - *intersectStart = (start1 >= start2) ? start1 : start2; - *intersectLength = (uint32) ((*intersectStart > intersectEnd) ? 0 : intersectEnd + 1 - *intersectStart); - - if (*intersectLength == 0) - *intersectStart = start1; -} - - -BOOL IsAccessibleByUser (PUNICODE_STRING objectFileName, BOOL readOnly) -{ - OBJECT_ATTRIBUTES fileObjAttributes; - IO_STATUS_BLOCK ioStatusBlock; - HANDLE fileHandle; - NTSTATUS status; - - ASSERT (!IoIsSystemThread (PsGetCurrentThread())); - - InitializeObjectAttributes (&fileObjAttributes, objectFileName, OBJ_CASE_INSENSITIVE | OBJ_FORCE_ACCESS_CHECK | OBJ_KERNEL_HANDLE, NULL, NULL); - - status = ZwCreateFile (&fileHandle, - readOnly ? GENERIC_READ : GENERIC_READ | GENERIC_WRITE, - &fileObjAttributes, - &ioStatusBlock, - NULL, - FILE_ATTRIBUTE_NORMAL, - FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, - FILE_OPEN, - FILE_SYNCHRONOUS_IO_NONALERT, - NULL, - 0); - - if (NT_SUCCESS (status)) - { - ZwClose (fileHandle); - return TRUE; - } - - return FALSE; -} - - -BOOL UserCanAccessDriveDevice () -{ - UNICODE_STRING name; - RtlInitUnicodeString (&name, L"\\Device\\MountPointManager"); - - return IsAccessibleByUser (&name, FALSE); -} - -BOOL IsDriveLetterAvailable (int nDosDriveNo, DeviceNamespaceType namespaceType) -{ - OBJECT_ATTRIBUTES objectAttributes; - UNICODE_STRING objectName; - WCHAR link[128]; - HANDLE handle; - NTSTATUS ntStatus; - - TCGetDosNameFromNumber (link, sizeof(link),nDosDriveNo, namespaceType); - RtlInitUnicodeString (&objectName, link); - InitializeObjectAttributes (&objectAttributes, &objectName, OBJ_KERNEL_HANDLE | OBJ_CASE_INSENSITIVE, NULL, NULL); - - if (NT_SUCCESS (ntStatus = ZwOpenSymbolicLinkObject (&handle, GENERIC_READ, &objectAttributes))) - { - ZwClose (handle); - return FALSE; - } - - return (ntStatus == STATUS_OBJECT_NAME_NOT_FOUND)? TRUE : FALSE; -} - - -NTSTATUS TCCompleteIrp (PIRP irp, NTSTATUS status, ULONG_PTR information) -{ - irp->IoStatus.Status = status; - irp->IoStatus.Information = information; - IoCompleteRequest (irp, IO_NO_INCREMENT); - return status; -} - - -NTSTATUS TCCompleteDiskIrp (PIRP irp, NTSTATUS status, ULONG_PTR information) -{ - irp->IoStatus.Status = status; - irp->IoStatus.Information = information; - IoCompleteRequest (irp, NT_SUCCESS (status) ? IO_DISK_INCREMENT : IO_NO_INCREMENT); - return status; -} - - -size_t GetCpuCount () -{ - KAFFINITY activeCpuMap = KeQueryActiveProcessors(); - size_t mapSize = sizeof (activeCpuMap) * 8; - size_t cpuCount = 0; - - while (mapSize--) - { - if (activeCpuMap & 1) - ++cpuCount; - - activeCpuMap >>= 1; - } - - if (cpuCount == 0) - return 1; - - return cpuCount; -} - - -void EnsureNullTerminatedString (wchar_t *str, size_t maxSizeInBytes) -{ - ASSERT ((maxSizeInBytes & 1) == 0); - str[maxSizeInBytes / sizeof (wchar_t) - 1] = 0; -} - - -void *AllocateMemoryWithTimeout (size_t size, int retryDelay, int timeout) -{ - LARGE_INTEGER waitInterval; - waitInterval.QuadPart = retryDelay * -10000; - - ASSERT (KeGetCurrentIrql() <= APC_LEVEL); - ASSERT (retryDelay > 0 && retryDelay <= timeout); - - while (TRUE) - { - void *memory = TCalloc (size); - if (memory) - return memory; - - timeout -= retryDelay; - if (timeout <= 0) - break; - - KeDelayExecutionThread (KernelMode, FALSE, &waitInterval); - } - - return NULL; -} - - -NTSTATUS TCReadRegistryKey (PUNICODE_STRING keyPath, wchar_t *keyValueName, PKEY_VALUE_PARTIAL_INFORMATION *keyData) -{ - OBJECT_ATTRIBUTES regObjAttribs; - HANDLE regKeyHandle; - NTSTATUS status; - UNICODE_STRING valName; - ULONG size = 0; - ULONG resultSize; - - InitializeObjectAttributes (®ObjAttribs, keyPath, OBJ_KERNEL_HANDLE | OBJ_CASE_INSENSITIVE, NULL, NULL); - status = ZwOpenKey (®KeyHandle, KEY_READ, ®ObjAttribs); - if (!NT_SUCCESS (status)) - return status; - - RtlInitUnicodeString (&valName, keyValueName); - status = ZwQueryValueKey (regKeyHandle, &valName, KeyValuePartialInformation, NULL, 0, &size); - - if (!NT_SUCCESS (status) && status != STATUS_BUFFER_OVERFLOW && status != STATUS_BUFFER_TOO_SMALL) - { - ZwClose (regKeyHandle); - return status; - } - - if (size == 0) - { - ZwClose (regKeyHandle); - return STATUS_NO_DATA_DETECTED; - } - - *keyData = (PKEY_VALUE_PARTIAL_INFORMATION) TCalloc (size); - if (!*keyData) - { - ZwClose (regKeyHandle); - return STATUS_INSUFFICIENT_RESOURCES; - } - - status = ZwQueryValueKey (regKeyHandle, &valName, KeyValuePartialInformation, *keyData, size, &resultSize); - - ZwClose (regKeyHandle); - return status; -} - - -NTSTATUS TCWriteRegistryKey (PUNICODE_STRING keyPath, wchar_t *keyValueName, ULONG keyValueType, void *valueData, ULONG valueSize) -{ - OBJECT_ATTRIBUTES regObjAttribs; - HANDLE regKeyHandle; - NTSTATUS status; - UNICODE_STRING valName; - - InitializeObjectAttributes (®ObjAttribs, keyPath, OBJ_KERNEL_HANDLE | OBJ_CASE_INSENSITIVE, NULL, NULL); - status = ZwOpenKey (®KeyHandle, KEY_READ | KEY_WRITE, ®ObjAttribs); - if (!NT_SUCCESS (status)) - return status; - - RtlInitUnicodeString (&valName, keyValueName); - - status = ZwSetValueKey (regKeyHandle, &valName, 0, keyValueType, valueData, valueSize); - - ZwClose (regKeyHandle); - return status; -} - - -BOOL IsVolumeClassFilterRegistered () -{ - UNICODE_STRING name; - NTSTATUS status; - BOOL registered = FALSE; - - PKEY_VALUE_PARTIAL_INFORMATION data; - - RtlInitUnicodeString (&name, L"\\REGISTRY\\MACHINE\\SYSTEM\\CurrentControlSet\\Control\\Class\\{71A27CDD-812A-11D0-BEC7-08002BE2092F}"); - status = TCReadRegistryKey (&name, L"UpperFilters", &data); - - if (NT_SUCCESS (status)) - { - if (data->Type == REG_MULTI_SZ && data->DataLength >= 9 * sizeof (wchar_t)) - { - // Search for the string "veracrypt" - ULONG i; - for (i = 0; i <= data->DataLength - 9 * sizeof (wchar_t); ++i) - { - if (memcmp (data->Data + i, L"veracrypt", 9 * sizeof (wchar_t)) == 0) - { - Dump ("Volume class filter active\n"); - registered = TRUE; - break; - } - } - } - - TCfree (data); - } - - return registered; -} - - -NTSTATUS ReadRegistryConfigFlags (BOOL driverEntry) -{ - PKEY_VALUE_PARTIAL_INFORMATION data; - UNICODE_STRING name; - NTSTATUS status; - uint32 flags = 0; - - RtlInitUnicodeString (&name, L"\\REGISTRY\\MACHINE\\SYSTEM\\CurrentControlSet\\Services\\veracrypt"); - status = TCReadRegistryKey (&name, TC_DRIVER_CONFIG_REG_VALUE_NAME, &data); - - if (NT_SUCCESS (status)) - { - if (data->Type == REG_DWORD) - { - flags = *(uint32 *) data->Data; - Dump ("Configuration flags = 0x%x\n", flags); - - if (driverEntry) - { - if (flags & (TC_DRIVER_CONFIG_CACHE_BOOT_PASSWORD | TC_DRIVER_CONFIG_CACHE_BOOT_PASSWORD_FOR_SYS_FAVORITES)) - CacheBootPassword = TRUE; - - if (flags & TC_DRIVER_CONFIG_DISABLE_NONADMIN_SYS_FAVORITES_ACCESS) - NonAdminSystemFavoritesAccessDisabled = TRUE; - - if (flags & TC_DRIVER_CONFIG_CACHE_BOOT_PIM) - CacheBootPim = TRUE; - } - - EnableHwEncryption ((flags & TC_DRIVER_CONFIG_DISABLE_HARDWARE_ENCRYPTION) ? FALSE : TRUE); - - EnableExtendedIoctlSupport = (flags & TC_DRIVER_CONFIG_ENABLE_EXTENDED_IOCTL)? TRUE : FALSE; - } - else - status = STATUS_INVALID_PARAMETER; - - TCfree (data); - } - - if (driverEntry && NT_SUCCESS (TCReadRegistryKey (&name, TC_ENCRYPTION_FREE_CPU_COUNT_REG_VALUE_NAME, &data))) - { - if (data->Type == REG_DWORD) - EncryptionThreadPoolFreeCpuCountLimit = *(uint32 *) data->Data; - - TCfree (data); - } - - return status; -} - - -NTSTATUS WriteRegistryConfigFlags (uint32 flags) -{ - UNICODE_STRING name; - RtlInitUnicodeString (&name, L"\\REGISTRY\\MACHINE\\SYSTEM\\CurrentControlSet\\Services\\veracrypt"); - - return TCWriteRegistryKey (&name, TC_DRIVER_CONFIG_REG_VALUE_NAME, REG_DWORD, &flags, sizeof (flags)); -} - - -NTSTATUS GetDeviceSectorSize (PDEVICE_OBJECT deviceObject, ULONG *bytesPerSector) -{ - NTSTATUS status; - DISK_GEOMETRY geometry; - - status = SendDeviceIoControlRequest (deviceObject, IOCTL_DISK_GET_DRIVE_GEOMETRY, NULL, 0, &geometry, sizeof (geometry)); - - if (!NT_SUCCESS (status)) - return status; - - *bytesPerSector = geometry.BytesPerSector; - return STATUS_SUCCESS; -} - - -NTSTATUS ZeroUnreadableSectors (PDEVICE_OBJECT deviceObject, LARGE_INTEGER startOffset, ULONG size, uint64 *zeroedSectorCount) -{ - NTSTATUS status; - ULONG sectorSize; - ULONG sectorCount; - byte *sectorBuffer = NULL; - - *zeroedSectorCount = 0; - - status = GetDeviceSectorSize (deviceObject, §orSize); - if (!NT_SUCCESS (status)) - return status; - - sectorBuffer = TCalloc (sectorSize); - if (!sectorBuffer) - return STATUS_INSUFFICIENT_RESOURCES; - - for (sectorCount = size / sectorSize; sectorCount > 0; --sectorCount, startOffset.QuadPart += sectorSize) - { - status = TCReadDevice (deviceObject, sectorBuffer, startOffset, sectorSize); - if (!NT_SUCCESS (status)) - { - Dump ("Zeroing sector at %I64d\n", startOffset.QuadPart); - memset (sectorBuffer, 0, sectorSize); - - status = TCWriteDevice (deviceObject, sectorBuffer, startOffset, sectorSize); - if (!NT_SUCCESS (status)) - goto err; - - ++(*zeroedSectorCount); - } - } - - status = STATUS_SUCCESS; - -err: - if (sectorBuffer) - TCfree (sectorBuffer); - - return status; -} - - -NTSTATUS ReadDeviceSkipUnreadableSectors (PDEVICE_OBJECT deviceObject, byte *buffer, LARGE_INTEGER startOffset, ULONG size, uint64 *badSectorCount) -{ - NTSTATUS status; - ULONG sectorSize; - ULONG sectorCount; - - *badSectorCount = 0; - - status = GetDeviceSectorSize (deviceObject, §orSize); - if (!NT_SUCCESS (status)) - return status; - - for (sectorCount = size / sectorSize; sectorCount > 0; --sectorCount, startOffset.QuadPart += sectorSize, buffer += sectorSize) - { - status = TCReadDevice (deviceObject, buffer, startOffset, sectorSize); - if (!NT_SUCCESS (status)) - { - Dump ("Skipping bad sector at %I64d\n", startOffset.QuadPart); - memset (buffer, 0, sectorSize); - ++(*badSectorCount); - } - } - - return STATUS_SUCCESS; -} - - -BOOL IsVolumeAccessibleByCurrentUser (PEXTENSION volumeDeviceExtension) -{ - SECURITY_SUBJECT_CONTEXT subContext; - PACCESS_TOKEN accessToken; - PTOKEN_USER tokenUser; - BOOL result = FALSE; - - if (IoIsSystemThread (PsGetCurrentThread()) - || UserCanAccessDriveDevice() - || !volumeDeviceExtension->UserSid - || (volumeDeviceExtension->SystemFavorite && !NonAdminSystemFavoritesAccessDisabled)) - { - return TRUE; - } - - SeCaptureSubjectContext (&subContext); - SeLockSubjectContext(&subContext); - if (subContext.ClientToken && subContext.ImpersonationLevel >= SecurityImpersonation) - accessToken = subContext.ClientToken; - else - accessToken = subContext.PrimaryToken; - - if (!accessToken) - goto ret; - - if (SeTokenIsAdmin (accessToken)) - { - result = TRUE; - goto ret; - } - - if (!NT_SUCCESS (SeQueryInformationToken (accessToken, TokenUser, &tokenUser))) - goto ret; - - result = RtlEqualSid (volumeDeviceExtension->UserSid, tokenUser->User.Sid); - ExFreePool (tokenUser); // Documented in newer versions of WDK - -ret: - SeUnlockSubjectContext(&subContext); - SeReleaseSubjectContext (&subContext); - return result; -} - - -void GetElapsedTimeInit (LARGE_INTEGER *lastPerfCounter) -{ - *lastPerfCounter = KeQueryPerformanceCounter (NULL); -} - - -// Returns elapsed time in microseconds since last call -int64 GetElapsedTime (LARGE_INTEGER *lastPerfCounter) -{ - LARGE_INTEGER freq; - LARGE_INTEGER counter = KeQueryPerformanceCounter (&freq); - - int64 elapsed = (counter.QuadPart - lastPerfCounter->QuadPart) * 1000000LL / freq.QuadPart; - *lastPerfCounter = counter; - - return elapsed; -} - - -BOOL IsOSAtLeast (OSVersionEnum reqMinOS) -{ - /* When updating this function, update IsOSVersionAtLeast() in Dlgcode.c too. */ - - ULONG major = 0, minor = 0; - - ASSERT (OsMajorVersion != 0); - - switch (reqMinOS) - { - case WIN_2000: major = 5; minor = 0; break; - case WIN_XP: major = 5; minor = 1; break; - case WIN_SERVER_2003: major = 5; minor = 2; break; - case WIN_VISTA: major = 6; minor = 0; break; - case WIN_7: major = 6; minor = 1; break; - case WIN_8: major = 6; minor = 2; break; - case WIN_8_1: major = 6; minor = 3; break; - case WIN_10: major = 10; minor = 0; break; - - default: - TC_THROW_FATAL_EXCEPTION; - break; - } - - return ((OsMajorVersion << 16 | OsMinorVersion << 8) - >= (major << 16 | minor << 8)); -} +/* + Legal Notice: Some portions of the source code contained in this file were + derived from the source code of TrueCrypt 7.1a, which is + Copyright (c) 2003-2012 TrueCrypt Developers Association and which is + governed by the TrueCrypt License 3.0, also from the source code of + Encryption for the Masses 2.02a, which is Copyright (c) 1998-2000 Paul Le Roux + and which is governed by the 'License Agreement for Encryption for the Masses' + Modifications and additions to the original source code (contained in this file) + and all other portions of this file are Copyright (c) 2013-2016 IDRIX + and are governed by the Apache License 2.0 the full text of which is + contained in the file License.txt included in VeraCrypt binary and source + code distribution packages. */ + +#include "TCdefs.h" +#include +#include "Crypto.h" +#include "Fat.h" +#include "Tests.h" +#include "cpu.h" + +#include "Apidrvr.h" +#include "Boot/Windows/BootDefs.h" +#include "EncryptedIoQueue.h" +#include "EncryptionThreadPool.h" +#include "Ntdriver.h" +#include "Ntvol.h" +#include "DriveFilter.h" +#include "DumpFilter.h" +#include "Cache.h" +#include "Volumes.h" +#include "VolumeFilter.h" + +#include +#include +#include +#include +#include + +#include +#include + +/* Init section, which is thrown away as soon as DriverEntry returns */ +#pragma alloc_text(INIT,DriverEntry) +#pragma alloc_text(INIT,TCCreateRootDeviceObject) + +PDRIVER_OBJECT TCDriverObject; +PDEVICE_OBJECT RootDeviceObject = NULL; +static KMUTEX RootDeviceControlMutex; +BOOL DriverShuttingDown = FALSE; +BOOL SelfTestsPassed; +int LastUniqueVolumeId; +ULONG OsMajorVersion = 0; +ULONG OsMinorVersion; +BOOL DriverUnloadDisabled = FALSE; +BOOL PortableMode = FALSE; +BOOL VolumeClassFilterRegistered = FALSE; +BOOL CacheBootPassword = FALSE; +BOOL CacheBootPim = FALSE; +BOOL NonAdminSystemFavoritesAccessDisabled = FALSE; +static size_t EncryptionThreadPoolFreeCpuCountLimit = 0; +static BOOL SystemFavoriteVolumeDirty = FALSE; +static BOOL PagingFileCreationPrevented = FALSE; +static BOOL EnableExtendedIoctlSupport = FALSE; + +PDEVICE_OBJECT VirtualVolumeDeviceObjects[MAX_MOUNTED_VOLUME_DRIVE_NUMBER + 1]; + + +NTSTATUS DriverEntry (PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegistryPath) +{ + PKEY_VALUE_PARTIAL_INFORMATION startKeyValue; + LONG version; + int i; + + Dump ("DriverEntry " TC_APP_NAME " " VERSION_STRING "\n"); + + DetectX86Features (); + + PsGetVersion (&OsMajorVersion, &OsMinorVersion, NULL, NULL); + + // Load dump filter if the main driver is already loaded + if (NT_SUCCESS (TCDeviceIoControl (NT_ROOT_PREFIX, TC_IOCTL_GET_DRIVER_VERSION, NULL, 0, &version, sizeof (version)))) + return DumpFilterEntry ((PFILTER_EXTENSION) DriverObject, (PFILTER_INITIALIZATION_DATA) RegistryPath); + + TCDriverObject = DriverObject; + memset (VirtualVolumeDeviceObjects, 0, sizeof (VirtualVolumeDeviceObjects)); + + ReadRegistryConfigFlags (TRUE); + EncryptionThreadPoolStart (EncryptionThreadPoolFreeCpuCountLimit); + SelfTestsPassed = AutoTestAlgorithms(); + + // Enable device class filters and load boot arguments if the driver is set to start at system boot + + if (NT_SUCCESS (TCReadRegistryKey (RegistryPath, L"Start", &startKeyValue))) + { + if (startKeyValue->Type == REG_DWORD && *((uint32 *) startKeyValue->Data) == SERVICE_BOOT_START) + { + if (!SelfTestsPassed) + TC_BUG_CHECK (STATUS_INVALID_PARAMETER); + + LoadBootArguments(); + VolumeClassFilterRegistered = IsVolumeClassFilterRegistered(); + + DriverObject->DriverExtension->AddDevice = DriverAddDevice; + } + + TCfree (startKeyValue); + } + + for (i = 0; i <= IRP_MJ_MAXIMUM_FUNCTION; ++i) + { + DriverObject->MajorFunction[i] = TCDispatchQueueIRP; + } + + DriverObject->DriverUnload = TCUnloadDriver; + return TCCreateRootDeviceObject (DriverObject); +} + + +NTSTATUS DriverAddDevice (PDRIVER_OBJECT driverObject, PDEVICE_OBJECT pdo) +{ +#ifdef DEBUG + char nameInfoBuffer[128]; + POBJECT_NAME_INFORMATION nameInfo = (POBJECT_NAME_INFORMATION) nameInfoBuffer; + ULONG nameInfoSize; + Dump ("AddDevice pdo=%p type=%x name=%ws\n", pdo, pdo->DeviceType, NT_SUCCESS (ObQueryNameString (pdo, nameInfo, sizeof (nameInfoBuffer), &nameInfoSize)) ? nameInfo->Name.Buffer : L"?"); +#endif + + if (VolumeClassFilterRegistered && BootArgsValid && BootArgs.HiddenSystemPartitionStart != 0) + { + PWSTR interfaceLinks = NULL; + if (NT_SUCCESS (IoGetDeviceInterfaces (&GUID_DEVINTERFACE_VOLUME, pdo, DEVICE_INTERFACE_INCLUDE_NONACTIVE, &interfaceLinks)) && interfaceLinks) + { + if (interfaceLinks[0] != UNICODE_NULL) + { + Dump ("Volume pdo=%p interface=%ws\n", pdo, interfaceLinks); + ExFreePool (interfaceLinks); + + return VolumeFilterAddDevice (driverObject, pdo); + } + + ExFreePool (interfaceLinks); + } + } + + return DriveFilterAddDevice (driverObject, pdo); +} + + +// Dumps a memory region to debug output +void DumpMemory (void *mem, int size) +{ + unsigned char str[20]; + unsigned char *m = mem; + int i,j; + + for (j = 0; j < size / 8; j++) + { + memset (str,0,sizeof str); + for (i = 0; i < 8; i++) + { + if (m[i] > ' ' && m[i] <= '~') + str[i]=m[i]; + else + str[i]='.'; + } + + Dump ("0x%08p %02x %02x %02x %02x %02x %02x %02x %02x %s\n", + m, m[0], m[1], m[2], m[3], m[4], m[5], m[6], m[7], str); + + m+=8; + } +} + + +BOOL ValidateIOBufferSize (PIRP irp, size_t requiredBufferSize, ValidateIOBufferSizeType type) +{ + PIO_STACK_LOCATION irpSp = IoGetCurrentIrpStackLocation (irp); + BOOL input = (type == ValidateInput || type == ValidateInputOutput); + BOOL output = (type == ValidateOutput || type == ValidateInputOutput); + + if ((input && irpSp->Parameters.DeviceIoControl.InputBufferLength < requiredBufferSize) + || (output && irpSp->Parameters.DeviceIoControl.OutputBufferLength < requiredBufferSize)) + { + Dump ("STATUS_BUFFER_TOO_SMALL ioctl=0x%x,%d in=%d out=%d reqsize=%d insize=%d outsize=%d\n", (int) (irpSp->Parameters.DeviceIoControl.IoControlCode >> 16), (int) ((irpSp->Parameters.DeviceIoControl.IoControlCode & 0x1FFF) >> 2), input, output, requiredBufferSize, irpSp->Parameters.DeviceIoControl.InputBufferLength, irpSp->Parameters.DeviceIoControl.OutputBufferLength); + + irp->IoStatus.Status = STATUS_BUFFER_TOO_SMALL; + irp->IoStatus.Information = 0; + return FALSE; + } + + if (!input && output) + memset (irp->AssociatedIrp.SystemBuffer, 0, irpSp->Parameters.DeviceIoControl.OutputBufferLength); + + return TRUE; +} + + +PDEVICE_OBJECT GetVirtualVolumeDeviceObject (int driveNumber) +{ + if (driveNumber < MIN_MOUNTED_VOLUME_DRIVE_NUMBER || driveNumber > MAX_MOUNTED_VOLUME_DRIVE_NUMBER) + return NULL; + + return VirtualVolumeDeviceObjects[driveNumber]; +} + + +/* TCDispatchQueueIRP queues any IRP's so that they can be processed later + by the thread -- or in some cases handles them immediately! */ +NTSTATUS TCDispatchQueueIRP (PDEVICE_OBJECT DeviceObject, PIRP Irp) +{ + PEXTENSION Extension = (PEXTENSION) DeviceObject->DeviceExtension; + PIO_STACK_LOCATION irpSp = IoGetCurrentIrpStackLocation (Irp); + NTSTATUS ntStatus; + +#ifdef _DEBUG + if (irpSp->MajorFunction == IRP_MJ_DEVICE_CONTROL && (Extension->bRootDevice || Extension->IsVolumeDevice)) + { + switch (irpSp->Parameters.DeviceIoControl.IoControlCode) + { + case TC_IOCTL_GET_MOUNTED_VOLUMES: + case TC_IOCTL_GET_PASSWORD_CACHE_STATUS: + case TC_IOCTL_GET_PORTABLE_MODE_STATUS: + case TC_IOCTL_SET_PORTABLE_MODE_STATUS: + case TC_IOCTL_OPEN_TEST: + case TC_IOCTL_GET_RESOLVED_SYMLINK: + case TC_IOCTL_GET_DEVICE_REFCOUNT: + case TC_IOCTL_GET_DRIVE_PARTITION_INFO: + case TC_IOCTL_GET_BOOT_DRIVE_VOLUME_PROPERTIES: + case TC_IOCTL_GET_BOOT_ENCRYPTION_STATUS: + case TC_IOCTL_GET_DECOY_SYSTEM_WIPE_STATUS: + case TC_IOCTL_GET_WARNING_FLAGS: + case TC_IOCTL_IS_HIDDEN_SYSTEM_RUNNING: + case IOCTL_DISK_CHECK_VERIFY: + break; + + default: + Dump ("%ls (0x%x %d)\n", + TCTranslateCode (irpSp->Parameters.DeviceIoControl.IoControlCode), + (int) (irpSp->Parameters.DeviceIoControl.IoControlCode >> 16), + (int) ((irpSp->Parameters.DeviceIoControl.IoControlCode & 0x1FFF) >> 2)); + } + } +#endif + + if (!Extension->bRootDevice) + { + // Drive filter IRP + if (Extension->IsDriveFilterDevice) + return DriveFilterDispatchIrp (DeviceObject, Irp); + + // Volume filter IRP + if (Extension->IsVolumeFilterDevice) + return VolumeFilterDispatchIrp (DeviceObject, Irp); + } + + switch (irpSp->MajorFunction) + { + case IRP_MJ_CLOSE: + case IRP_MJ_CREATE: + case IRP_MJ_CLEANUP: + return COMPLETE_IRP (DeviceObject, Irp, STATUS_SUCCESS, 0); + + case IRP_MJ_SHUTDOWN: + if (Extension->bRootDevice) + { + Dump ("Driver shutting down\n"); + DriverShuttingDown = TRUE; + + if (EncryptionSetupThread) + while (SendDeviceIoControlRequest (RootDeviceObject, TC_IOCTL_ABORT_BOOT_ENCRYPTION_SETUP, NULL, 0, NULL, 0) == STATUS_INSUFFICIENT_RESOURCES); + + if (DecoySystemWipeThread) + while (SendDeviceIoControlRequest (RootDeviceObject, TC_IOCTL_ABORT_DECOY_SYSTEM_WIPE, NULL, 0, NULL, 0) == STATUS_INSUFFICIENT_RESOURCES); + + OnShutdownPending(); + } + + return COMPLETE_IRP (DeviceObject, Irp, STATUS_SUCCESS, 0); + + case IRP_MJ_FLUSH_BUFFERS: + case IRP_MJ_READ: + case IRP_MJ_WRITE: + case IRP_MJ_DEVICE_CONTROL: + + if (Extension->bRootDevice) + { + if (irpSp->MajorFunction == IRP_MJ_DEVICE_CONTROL) + { + NTSTATUS status = KeWaitForMutexObject (&RootDeviceControlMutex, Executive, KernelMode, FALSE, NULL); + if (!NT_SUCCESS (status)) + return status; + + status = ProcessMainDeviceControlIrp (DeviceObject, Extension, Irp); + + KeReleaseMutex (&RootDeviceControlMutex, FALSE); + return status; + } + break; + } + + if (Extension->bShuttingDown) + { + Dump ("Device %d shutting down: STATUS_DELETE_PENDING\n", Extension->nDosDriveNo); + return TCCompleteDiskIrp (Irp, STATUS_DELETE_PENDING, 0); + } + + if (Extension->bRemovable + && (DeviceObject->Flags & DO_VERIFY_VOLUME) + && !(irpSp->Flags & SL_OVERRIDE_VERIFY_VOLUME) + && irpSp->MajorFunction != IRP_MJ_FLUSH_BUFFERS) + { + Dump ("Removable device %d has DO_VERIFY_VOLUME flag: STATUS_DEVICE_NOT_READY\n", Extension->nDosDriveNo); + return TCCompleteDiskIrp (Irp, STATUS_DEVICE_NOT_READY, 0); + } + + switch (irpSp->MajorFunction) + { + case IRP_MJ_READ: + case IRP_MJ_WRITE: + ntStatus = EncryptedIoQueueAddIrp (&Extension->Queue, Irp); + + if (ntStatus != STATUS_PENDING) + TCCompleteDiskIrp (Irp, ntStatus, 0); + + return ntStatus; + + case IRP_MJ_DEVICE_CONTROL: + ntStatus = IoAcquireRemoveLock (&Extension->Queue.RemoveLock, Irp); + if (!NT_SUCCESS (ntStatus)) + return TCCompleteIrp (Irp, ntStatus, 0); + + IoMarkIrpPending (Irp); + + ExInterlockedInsertTailList (&Extension->ListEntry, &Irp->Tail.Overlay.ListEntry, &Extension->ListSpinLock); + KeReleaseSemaphore (&Extension->RequestSemaphore, IO_DISK_INCREMENT, 1, FALSE); + + return STATUS_PENDING; + + case IRP_MJ_FLUSH_BUFFERS: + return TCCompleteDiskIrp (Irp, STATUS_SUCCESS, 0); + } + + break; + + case IRP_MJ_PNP: + if (!Extension->bRootDevice + && Extension->IsVolumeDevice + && irpSp->MinorFunction == IRP_MN_DEVICE_USAGE_NOTIFICATION + && irpSp->Parameters.UsageNotification.Type == DeviceUsageTypePaging + && irpSp->Parameters.UsageNotification.InPath) + { + PagingFileCreationPrevented = TRUE; + return TCCompleteIrp (Irp, STATUS_UNSUCCESSFUL, 0); + } + break; + } + + return TCCompleteIrp (Irp, STATUS_INVALID_DEVICE_REQUEST, 0); +} + +NTSTATUS TCCreateRootDeviceObject (PDRIVER_OBJECT DriverObject) +{ + UNICODE_STRING Win32NameString, ntUnicodeString; + WCHAR dosname[32], ntname[32]; + PDEVICE_OBJECT DeviceObject; + NTSTATUS ntStatus; + BOOL *bRootExtension; + + Dump ("TCCreateRootDeviceObject BEGIN\n"); + ASSERT (KeGetCurrentIrql() == PASSIVE_LEVEL); + + RtlStringCbCopyW (dosname, sizeof(dosname),(LPWSTR) DOS_ROOT_PREFIX); + RtlStringCbCopyW (ntname, sizeof(ntname),(LPWSTR) NT_ROOT_PREFIX); + RtlInitUnicodeString (&ntUnicodeString, ntname); + RtlInitUnicodeString (&Win32NameString, dosname); + + Dump ("Creating root device nt=%ls dos=%ls\n", ntname, dosname); + + ntStatus = IoCreateDevice ( + DriverObject, + sizeof (BOOL), + &ntUnicodeString, + FILE_DEVICE_UNKNOWN, + FILE_DEVICE_SECURE_OPEN, + FALSE, + &DeviceObject); + + if (!NT_SUCCESS (ntStatus)) + { + Dump ("TCCreateRootDeviceObject NTSTATUS = 0x%08x END\n", ntStatus); + return ntStatus;/* Failed to create DeviceObject */ + } + + DeviceObject->Flags |= DO_DIRECT_IO; + DeviceObject->AlignmentRequirement = FILE_WORD_ALIGNMENT; + + /* Setup the device extension */ + bRootExtension = (BOOL *) DeviceObject->DeviceExtension; + *bRootExtension = TRUE; + + KeInitializeMutex (&RootDeviceControlMutex, 0); + + ntStatus = IoCreateSymbolicLink (&Win32NameString, &ntUnicodeString); + + if (!NT_SUCCESS (ntStatus)) + { + Dump ("TCCreateRootDeviceObject NTSTATUS = 0x%08x END\n", ntStatus); + IoDeleteDevice (DeviceObject); + return ntStatus; + } + + IoRegisterShutdownNotification (DeviceObject); + RootDeviceObject = DeviceObject; + + Dump ("TCCreateRootDeviceObject STATUS_SUCCESS END\n"); + return STATUS_SUCCESS; +} + +NTSTATUS TCCreateDeviceObject (PDRIVER_OBJECT DriverObject, + PDEVICE_OBJECT * ppDeviceObject, + MOUNT_STRUCT * mount) +{ + UNICODE_STRING ntUnicodeString; + WCHAR ntname[32]; + PEXTENSION Extension; + NTSTATUS ntStatus; + ULONG devChars = 0; +#if defined (DEBUG) + WCHAR dosname[32]; +#endif + + Dump ("TCCreateDeviceObject BEGIN\n"); + ASSERT (KeGetCurrentIrql() == PASSIVE_LEVEL); + + TCGetNTNameFromNumber (ntname, sizeof(ntname),mount->nDosDriveNo); + RtlInitUnicodeString (&ntUnicodeString, ntname); +#if defined (DEBUG) + TCGetDosNameFromNumber (dosname, sizeof(dosname),mount->nDosDriveNo, DeviceNamespaceDefault); +#endif + + devChars = FILE_DEVICE_SECURE_OPEN; + devChars |= mount->bMountReadOnly ? FILE_READ_ONLY_DEVICE : 0; + devChars |= mount->bMountRemovable ? FILE_REMOVABLE_MEDIA : 0; + + Dump ("Creating device nt=%ls dos=%ls\n", ntname, dosname); + + ntStatus = IoCreateDevice ( + DriverObject, /* Our Driver Object */ + sizeof (EXTENSION), /* Size of state information */ + &ntUnicodeString, /* Device name "\Device\Name" */ + FILE_DEVICE_DISK, /* Device type */ + devChars, /* Device characteristics */ + FALSE, /* Exclusive device */ + ppDeviceObject); /* Returned ptr to Device Object */ + + if (!NT_SUCCESS (ntStatus)) + { + Dump ("TCCreateDeviceObject NTSTATUS = 0x%08x END\n", ntStatus); + return ntStatus;/* Failed to create DeviceObject */ + } + /* Initialize device object and extension. */ + + (*ppDeviceObject)->Flags |= DO_DIRECT_IO; + (*ppDeviceObject)->StackSize += 6; // Reduce occurrence of NO_MORE_IRP_STACK_LOCATIONS bug check caused by buggy drivers + + /* Setup the device extension */ + Extension = (PEXTENSION) (*ppDeviceObject)->DeviceExtension; + memset (Extension, 0, sizeof (EXTENSION)); + + Extension->IsVolumeDevice = TRUE; + Extension->nDosDriveNo = mount->nDosDriveNo; + Extension->bRemovable = mount->bMountRemovable; + Extension->PartitionInInactiveSysEncScope = mount->bPartitionInInactiveSysEncScope; + Extension->SystemFavorite = mount->SystemFavorite; + + KeInitializeEvent (&Extension->keCreateEvent, SynchronizationEvent, FALSE); + KeInitializeSemaphore (&Extension->RequestSemaphore, 0L, MAXLONG); + KeInitializeSpinLock (&Extension->ListSpinLock); + InitializeListHead (&Extension->ListEntry); + IoInitializeRemoveLock (&Extension->Queue.RemoveLock, 'LRCV', 0, 0); + + VirtualVolumeDeviceObjects[mount->nDosDriveNo] = *ppDeviceObject; + + Dump ("TCCreateDeviceObject STATUS_SUCCESS END\n"); + + return STATUS_SUCCESS; +} + + +BOOL RootDeviceControlMutexAcquireNoWait () +{ + NTSTATUS status; + LARGE_INTEGER timeout; + timeout.QuadPart = 0; + + status = KeWaitForMutexObject (&RootDeviceControlMutex, Executive, KernelMode, FALSE, &timeout); + return NT_SUCCESS (status) && status != STATUS_TIMEOUT; +} + + +void RootDeviceControlMutexRelease () +{ + KeReleaseMutex (&RootDeviceControlMutex, FALSE); +} + + +NTSTATUS ProcessVolumeDeviceControlIrp (PDEVICE_OBJECT DeviceObject, PEXTENSION Extension, PIRP Irp) +{ + PIO_STACK_LOCATION irpSp = IoGetCurrentIrpStackLocation (Irp); + + switch (irpSp->Parameters.DeviceIoControl.IoControlCode) + { + + case IOCTL_MOUNTDEV_QUERY_DEVICE_NAME: + if (!ValidateIOBufferSize (Irp, sizeof (MOUNTDEV_NAME), ValidateOutput)) + { + Irp->IoStatus.Information = sizeof (MOUNTDEV_NAME); + Irp->IoStatus.Status = STATUS_BUFFER_OVERFLOW; + } + else + { + ULONG outLength; + UNICODE_STRING ntUnicodeString; + WCHAR ntName[256]; + PMOUNTDEV_NAME outputBuffer = (PMOUNTDEV_NAME) Irp->AssociatedIrp.SystemBuffer; + + TCGetNTNameFromNumber (ntName, sizeof(ntName),Extension->nDosDriveNo); + RtlInitUnicodeString (&ntUnicodeString, ntName); + + outputBuffer->NameLength = ntUnicodeString.Length; + outLength = ntUnicodeString.Length + sizeof(USHORT); + + if (irpSp->Parameters.DeviceIoControl.OutputBufferLength < outLength) + { + Irp->IoStatus.Information = sizeof (MOUNTDEV_NAME); + Irp->IoStatus.Status = STATUS_BUFFER_OVERFLOW; + + break; + } + + RtlCopyMemory ((PCHAR)outputBuffer->Name,ntUnicodeString.Buffer, ntUnicodeString.Length); + + Irp->IoStatus.Status = STATUS_SUCCESS; + Irp->IoStatus.Information = outLength; + + Dump ("name = %ls\n",ntName); + } + break; + + case IOCTL_MOUNTDEV_QUERY_UNIQUE_ID: + if (!ValidateIOBufferSize (Irp, sizeof (MOUNTDEV_UNIQUE_ID), ValidateOutput)) + { + Irp->IoStatus.Information = sizeof (MOUNTDEV_UNIQUE_ID); + Irp->IoStatus.Status = STATUS_BUFFER_OVERFLOW; + } + else + { + ULONG outLength; + UCHAR volId[128], tmp[] = { 0,0 }; + PMOUNTDEV_UNIQUE_ID outputBuffer = (PMOUNTDEV_UNIQUE_ID) Irp->AssociatedIrp.SystemBuffer; + + RtlStringCbCopyA (volId, sizeof(volId),TC_UNIQUE_ID_PREFIX); + tmp[0] = 'A' + (UCHAR) Extension->nDosDriveNo; + RtlStringCbCatA (volId, sizeof(volId),tmp); + + outputBuffer->UniqueIdLength = (USHORT) strlen (volId); + outLength = (ULONG) (strlen (volId) + sizeof (USHORT)); + + if (irpSp->Parameters.DeviceIoControl.OutputBufferLength < outLength) + { + Irp->IoStatus.Information = sizeof (MOUNTDEV_UNIQUE_ID); + Irp->IoStatus.Status = STATUS_BUFFER_OVERFLOW; + break; + } + + RtlCopyMemory ((PCHAR)outputBuffer->UniqueId, volId, strlen (volId)); + + Irp->IoStatus.Status = STATUS_SUCCESS; + Irp->IoStatus.Information = outLength; + + Dump ("id = %s\n",volId); + } + break; + + case IOCTL_MOUNTDEV_QUERY_SUGGESTED_LINK_NAME: + { + ULONG outLength; + UNICODE_STRING ntUnicodeString; + WCHAR ntName[256]; + PMOUNTDEV_SUGGESTED_LINK_NAME outputBuffer = (PMOUNTDEV_SUGGESTED_LINK_NAME) Irp->AssociatedIrp.SystemBuffer; + + if (!ValidateIOBufferSize (Irp, sizeof (MOUNTDEV_SUGGESTED_LINK_NAME), ValidateOutput)) + { + Irp->IoStatus.Status = STATUS_INVALID_PARAMETER; + Irp->IoStatus.Information = 0; + break; + } + + TCGetDosNameFromNumber (ntName, sizeof(ntName),Extension->nDosDriveNo, DeviceNamespaceDefault); + RtlInitUnicodeString (&ntUnicodeString, ntName); + + outLength = FIELD_OFFSET(MOUNTDEV_SUGGESTED_LINK_NAME,Name) + ntUnicodeString.Length; + + outputBuffer->UseOnlyIfThereAreNoOtherLinks = FALSE; + outputBuffer->NameLength = ntUnicodeString.Length; + + if(irpSp->Parameters.DeviceIoControl.OutputBufferLength < outLength) + { + Irp->IoStatus.Information = sizeof (MOUNTDEV_SUGGESTED_LINK_NAME); + Irp->IoStatus.Status = STATUS_BUFFER_OVERFLOW; + break; + } + + RtlCopyMemory ((PCHAR)outputBuffer->Name,ntUnicodeString.Buffer, ntUnicodeString.Length); + + Irp->IoStatus.Status = STATUS_SUCCESS; + Irp->IoStatus.Information = outLength; + + Dump ("link = %ls\n",ntName); + } + break; + + case IOCTL_DISK_GET_MEDIA_TYPES: + case IOCTL_DISK_GET_DRIVE_GEOMETRY: + /* Return the drive geometry for the disk. Note that we + return values which were made up to suit the disk size. */ + if (ValidateIOBufferSize (Irp, sizeof (DISK_GEOMETRY), ValidateOutput)) + { + PDISK_GEOMETRY outputBuffer = (PDISK_GEOMETRY) + Irp->AssociatedIrp.SystemBuffer; + + outputBuffer->MediaType = Extension->bRemovable ? RemovableMedia : FixedMedia; + outputBuffer->Cylinders.QuadPart = Extension->NumberOfCylinders; + outputBuffer->TracksPerCylinder = Extension->TracksPerCylinder; + outputBuffer->SectorsPerTrack = Extension->SectorsPerTrack; + outputBuffer->BytesPerSector = Extension->BytesPerSector; + Irp->IoStatus.Status = STATUS_SUCCESS; + Irp->IoStatus.Information = sizeof (DISK_GEOMETRY); + } + break; + + case IOCTL_STORAGE_QUERY_PROPERTY: + if (EnableExtendedIoctlSupport) + { + if (ValidateIOBufferSize (Irp, sizeof (STORAGE_PROPERTY_QUERY), ValidateInput)) + { + PSTORAGE_PROPERTY_QUERY pStoragePropQuery = (PSTORAGE_PROPERTY_QUERY) Irp->AssociatedIrp.SystemBuffer; + STORAGE_QUERY_TYPE type = pStoragePropQuery->QueryType; + + /* return error if an unsupported type is encountered */ + Irp->IoStatus.Status = STATUS_INVALID_DEVICE_REQUEST; + Irp->IoStatus.Information = 0; + + if ( (pStoragePropQuery->PropertyId == StorageAccessAlignmentProperty) + || (pStoragePropQuery->PropertyId == StorageDeviceProperty) + ) + { + if (type == PropertyExistsQuery) + { + Irp->IoStatus.Status = STATUS_SUCCESS; + Irp->IoStatus.Information = 0; + } + else if (type == PropertyStandardQuery) + { + switch (pStoragePropQuery->PropertyId) + { + case StorageDeviceProperty: + { + if (ValidateIOBufferSize (Irp, sizeof (STORAGE_DEVICE_DESCRIPTOR), ValidateOutput)) + { + PSTORAGE_DEVICE_DESCRIPTOR outputBuffer = (PSTORAGE_DEVICE_DESCRIPTOR) Irp->AssociatedIrp.SystemBuffer; + + outputBuffer->Version = sizeof(STORAGE_DEVICE_DESCRIPTOR); + outputBuffer->Size = sizeof(STORAGE_DEVICE_DESCRIPTOR); + outputBuffer->DeviceType = FILE_DEVICE_DISK; + outputBuffer->RemovableMedia = Extension->bRemovable? TRUE : FALSE; + Irp->IoStatus.Status = STATUS_SUCCESS; + Irp->IoStatus.Information = sizeof (STORAGE_DEVICE_DESCRIPTOR); + } + } + break; + case StorageAccessAlignmentProperty: + { + if (ValidateIOBufferSize (Irp, sizeof (STORAGE_ACCESS_ALIGNMENT_DESCRIPTOR), ValidateOutput)) + { + PSTORAGE_ACCESS_ALIGNMENT_DESCRIPTOR outputBuffer = (PSTORAGE_ACCESS_ALIGNMENT_DESCRIPTOR) Irp->AssociatedIrp.SystemBuffer; + + outputBuffer->Version = sizeof(STORAGE_ACCESS_ALIGNMENT_DESCRIPTOR); + outputBuffer->Size = sizeof(STORAGE_ACCESS_ALIGNMENT_DESCRIPTOR); + outputBuffer->BytesPerLogicalSector = Extension->BytesPerSector; + outputBuffer->BytesPerPhysicalSector = Extension->HostBytesPerPhysicalSector; + outputBuffer->BytesOffsetForSectorAlignment = Extension->BytesOffsetForSectorAlignment; + Irp->IoStatus.Status = STATUS_SUCCESS; + Irp->IoStatus.Information = sizeof (STORAGE_ACCESS_ALIGNMENT_DESCRIPTOR); + } + } + break; + } + } + } + } + } + else + return TCCompleteIrp (Irp, STATUS_INVALID_DEVICE_REQUEST, 0); + + break; + + case IOCTL_DISK_GET_PARTITION_INFO: + if (ValidateIOBufferSize (Irp, sizeof (PARTITION_INFORMATION), ValidateOutput)) + { + PPARTITION_INFORMATION outputBuffer = (PPARTITION_INFORMATION) + Irp->AssociatedIrp.SystemBuffer; + + outputBuffer->PartitionType = Extension->PartitionType; + outputBuffer->BootIndicator = FALSE; + outputBuffer->RecognizedPartition = TRUE; + outputBuffer->RewritePartition = FALSE; + outputBuffer->StartingOffset.QuadPart = Extension->BytesPerSector; + outputBuffer->PartitionLength.QuadPart= Extension->DiskLength; + outputBuffer->HiddenSectors = 0; + Irp->IoStatus.Status = STATUS_SUCCESS; + Irp->IoStatus.Information = sizeof (PARTITION_INFORMATION); + } + break; + + case IOCTL_DISK_GET_PARTITION_INFO_EX: + if (ValidateIOBufferSize (Irp, sizeof (PARTITION_INFORMATION_EX), ValidateOutput)) + { + PPARTITION_INFORMATION_EX outputBuffer = (PPARTITION_INFORMATION_EX) Irp->AssociatedIrp.SystemBuffer; + + outputBuffer->PartitionStyle = PARTITION_STYLE_MBR; + outputBuffer->RewritePartition = FALSE; + outputBuffer->StartingOffset.QuadPart = Extension->BytesPerSector; + outputBuffer->PartitionLength.QuadPart= Extension->DiskLength; + outputBuffer->Mbr.PartitionType = Extension->PartitionType; + outputBuffer->Mbr.BootIndicator = FALSE; + outputBuffer->Mbr.RecognizedPartition = TRUE; + outputBuffer->Mbr.HiddenSectors = 0; + Irp->IoStatus.Status = STATUS_SUCCESS; + Irp->IoStatus.Information = sizeof (PARTITION_INFORMATION_EX); + } + break; + + case IOCTL_DISK_GET_DRIVE_LAYOUT: + if (ValidateIOBufferSize (Irp, sizeof (DRIVE_LAYOUT_INFORMATION), ValidateOutput)) + { + PDRIVE_LAYOUT_INFORMATION outputBuffer = (PDRIVE_LAYOUT_INFORMATION) + Irp->AssociatedIrp.SystemBuffer; + + outputBuffer->PartitionCount = 1; + outputBuffer->Signature = 0; + + outputBuffer->PartitionEntry->PartitionType = Extension->PartitionType; + outputBuffer->PartitionEntry->BootIndicator = FALSE; + outputBuffer->PartitionEntry->RecognizedPartition = TRUE; + outputBuffer->PartitionEntry->RewritePartition = FALSE; + outputBuffer->PartitionEntry->StartingOffset.QuadPart = Extension->BytesPerSector; + outputBuffer->PartitionEntry->PartitionLength.QuadPart = Extension->DiskLength; + outputBuffer->PartitionEntry->HiddenSectors = 0; + + Irp->IoStatus.Status = STATUS_SUCCESS; + Irp->IoStatus.Information = sizeof (PARTITION_INFORMATION); + } + break; + + case IOCTL_DISK_GET_LENGTH_INFO: + if (!ValidateIOBufferSize (Irp, sizeof (GET_LENGTH_INFORMATION), ValidateOutput)) + { + Irp->IoStatus.Status = STATUS_BUFFER_OVERFLOW; + Irp->IoStatus.Information = sizeof (GET_LENGTH_INFORMATION); + } + else + { + PGET_LENGTH_INFORMATION outputBuffer = (PGET_LENGTH_INFORMATION) Irp->AssociatedIrp.SystemBuffer; + + outputBuffer->Length.QuadPart = Extension->DiskLength; + Irp->IoStatus.Status = STATUS_SUCCESS; + Irp->IoStatus.Information = sizeof (GET_LENGTH_INFORMATION); + } + break; + + case IOCTL_DISK_VERIFY: + if (ValidateIOBufferSize (Irp, sizeof (VERIFY_INFORMATION), ValidateInput)) + { + HRESULT hResult; + ULONGLONG ullStartingOffset, ullNewOffset, ullEndOffset; + PVERIFY_INFORMATION pVerifyInformation; + pVerifyInformation = (PVERIFY_INFORMATION) Irp->AssociatedIrp.SystemBuffer; + + ullStartingOffset = (ULONGLONG) pVerifyInformation->StartingOffset.QuadPart; + hResult = ULongLongAdd(ullStartingOffset, + (ULONGLONG) Extension->cryptoInfo->hiddenVolume ? Extension->cryptoInfo->hiddenVolumeOffset : Extension->cryptoInfo->volDataAreaOffset, + &ullNewOffset); + if (hResult != S_OK) + Irp->IoStatus.Status = STATUS_INVALID_PARAMETER; + else if (S_OK != ULongLongAdd(ullStartingOffset, (ULONGLONG) pVerifyInformation->Length, &ullEndOffset)) + Irp->IoStatus.Status = STATUS_INVALID_PARAMETER; + else if (ullEndOffset > (ULONGLONG) Extension->DiskLength) + Irp->IoStatus.Status = STATUS_INVALID_PARAMETER; + else + { + IO_STATUS_BLOCK ioStatus; + PVOID buffer = TCalloc (max (pVerifyInformation->Length, PAGE_SIZE)); + + if (!buffer) + { + Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES; + } + else + { + LARGE_INTEGER offset = pVerifyInformation->StartingOffset; + offset.QuadPart = ullNewOffset; + + Irp->IoStatus.Status = ZwReadFile (Extension->hDeviceFile, NULL, NULL, NULL, &ioStatus, buffer, pVerifyInformation->Length, &offset, NULL); + TCfree (buffer); + + if (NT_SUCCESS (Irp->IoStatus.Status) && ioStatus.Information != pVerifyInformation->Length) + Irp->IoStatus.Status = STATUS_INVALID_PARAMETER; + } + } + + Irp->IoStatus.Information = 0; + } + break; + + case IOCTL_DISK_CHECK_VERIFY: + case IOCTL_STORAGE_CHECK_VERIFY: + { + Irp->IoStatus.Status = STATUS_SUCCESS; + Irp->IoStatus.Information = 0; + + if (irpSp->Parameters.DeviceIoControl.OutputBufferLength >= sizeof (ULONG)) + { + *((ULONG *) Irp->AssociatedIrp.SystemBuffer) = 0; + Irp->IoStatus.Information = sizeof (ULONG); + } + } + break; + + case IOCTL_DISK_IS_WRITABLE: + { + if (Extension->bReadOnly) + Irp->IoStatus.Status = STATUS_MEDIA_WRITE_PROTECTED; + else + Irp->IoStatus.Status = STATUS_SUCCESS; + Irp->IoStatus.Information = 0; + + } + break; + + case IOCTL_VOLUME_ONLINE: + Irp->IoStatus.Status = STATUS_SUCCESS; + Irp->IoStatus.Information = 0; + break; + + case IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS: + + // Vista's filesystem defragmenter fails if IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS does not succeed. + if (!(OsMajorVersion == 6 && OsMinorVersion == 0)) + { + Irp->IoStatus.Status = STATUS_INVALID_DEVICE_REQUEST; + Irp->IoStatus.Information = 0; + } + else if (ValidateIOBufferSize (Irp, sizeof (VOLUME_DISK_EXTENTS), ValidateOutput)) + { + VOLUME_DISK_EXTENTS *extents = (VOLUME_DISK_EXTENTS *) Irp->AssociatedIrp.SystemBuffer; + + // No extent data can be returned as this is not a physical drive. + memset (extents, 0, sizeof (*extents)); + extents->NumberOfDiskExtents = 0; + + Irp->IoStatus.Status = STATUS_SUCCESS; + Irp->IoStatus.Information = sizeof (*extents); + } + break; + + default: + return TCCompleteIrp (Irp, STATUS_INVALID_DEVICE_REQUEST, 0); + } + +#ifdef DEBUG + if (!NT_SUCCESS (Irp->IoStatus.Status)) + { + Dump ("IOCTL error 0x%08x (0x%x %d)\n", + Irp->IoStatus.Status, + (int) (irpSp->Parameters.DeviceIoControl.IoControlCode >> 16), + (int) ((irpSp->Parameters.DeviceIoControl.IoControlCode & 0x1FFF) >> 2)); + } +#endif + + return TCCompleteDiskIrp (Irp, Irp->IoStatus.Status, Irp->IoStatus.Information); +} + + +NTSTATUS ProcessMainDeviceControlIrp (PDEVICE_OBJECT DeviceObject, PEXTENSION Extension, PIRP Irp) +{ + PIO_STACK_LOCATION irpSp = IoGetCurrentIrpStackLocation (Irp); + NTSTATUS ntStatus; + + switch (irpSp->Parameters.DeviceIoControl.IoControlCode) + { + case TC_IOCTL_GET_DRIVER_VERSION: + case TC_IOCTL_LEGACY_GET_DRIVER_VERSION: + if (ValidateIOBufferSize (Irp, sizeof (LONG), ValidateOutput)) + { + LONG tmp = VERSION_NUM; + memcpy (Irp->AssociatedIrp.SystemBuffer, &tmp, 4); + Irp->IoStatus.Information = sizeof (LONG); + Irp->IoStatus.Status = STATUS_SUCCESS; + } + break; + + case TC_IOCTL_GET_DEVICE_REFCOUNT: + if (ValidateIOBufferSize (Irp, sizeof (int), ValidateOutput)) + { + *(int *) Irp->AssociatedIrp.SystemBuffer = DeviceObject->ReferenceCount; + Irp->IoStatus.Information = sizeof (int); + Irp->IoStatus.Status = STATUS_SUCCESS; + } + break; + + case TC_IOCTL_IS_DRIVER_UNLOAD_DISABLED: + if (ValidateIOBufferSize (Irp, sizeof (int), ValidateOutput)) + { + LONG deviceObjectCount = 0; + + *(int *) Irp->AssociatedIrp.SystemBuffer = DriverUnloadDisabled; + + if (IoEnumerateDeviceObjectList (TCDriverObject, NULL, 0, &deviceObjectCount) == STATUS_BUFFER_TOO_SMALL && deviceObjectCount > 1) + *(int *) Irp->AssociatedIrp.SystemBuffer = TRUE; + + Irp->IoStatus.Information = sizeof (int); + Irp->IoStatus.Status = STATUS_SUCCESS; + } + break; + + case TC_IOCTL_IS_ANY_VOLUME_MOUNTED: + if (ValidateIOBufferSize (Irp, sizeof (int), ValidateOutput)) + { + int drive; + *(int *) Irp->AssociatedIrp.SystemBuffer = 0; + + for (drive = MIN_MOUNTED_VOLUME_DRIVE_NUMBER; drive <= MAX_MOUNTED_VOLUME_DRIVE_NUMBER; ++drive) + { + if (GetVirtualVolumeDeviceObject (drive)) + { + *(int *) Irp->AssociatedIrp.SystemBuffer = 1; + break; + } + } + + if (IsBootDriveMounted()) + *(int *) Irp->AssociatedIrp.SystemBuffer = 1; + + Irp->IoStatus.Information = sizeof (int); + Irp->IoStatus.Status = STATUS_SUCCESS; + } + break; + + case TC_IOCTL_OPEN_TEST: + { + OPEN_TEST_STRUCT *opentest = (OPEN_TEST_STRUCT *) Irp->AssociatedIrp.SystemBuffer; + OBJECT_ATTRIBUTES ObjectAttributes; + HANDLE NtFileHandle; + UNICODE_STRING FullFileName; + IO_STATUS_BLOCK IoStatus; + LARGE_INTEGER offset; + ACCESS_MASK access = FILE_READ_ATTRIBUTES; + + if (!ValidateIOBufferSize (Irp, sizeof (OPEN_TEST_STRUCT), ValidateInputOutput)) + break; + + EnsureNullTerminatedString (opentest->wszFileName, sizeof (opentest->wszFileName)); + RtlInitUnicodeString (&FullFileName, opentest->wszFileName); + + InitializeObjectAttributes (&ObjectAttributes, &FullFileName, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, NULL, NULL); + + if (opentest->bDetectTCBootLoader || opentest->DetectFilesystem || opentest->bMatchVolumeID) + access |= FILE_READ_DATA; + + ntStatus = ZwCreateFile (&NtFileHandle, + SYNCHRONIZE | access, &ObjectAttributes, &IoStatus, NULL, + 0, FILE_SHARE_READ | FILE_SHARE_WRITE, FILE_OPEN, FILE_SYNCHRONOUS_IO_NONALERT, NULL, 0); + + if (NT_SUCCESS (ntStatus)) + { + opentest->TCBootLoaderDetected = FALSE; + opentest->FilesystemDetected = FALSE; + opentest->VolumeIDMatched = FALSE; + + if (opentest->bDetectTCBootLoader || opentest->DetectFilesystem || opentest->bMatchVolumeID) + { + byte *readBuffer = TCalloc (TC_MAX_VOLUME_SECTOR_SIZE); + if (!readBuffer) + { + ntStatus = STATUS_INSUFFICIENT_RESOURCES; + } + else + { + if (opentest->bDetectTCBootLoader || opentest->DetectFilesystem) + { + // Determine if the first sector contains a portion of the VeraCrypt Boot Loader + + offset.QuadPart = 0; + + ntStatus = ZwReadFile (NtFileHandle, + NULL, + NULL, + NULL, + &IoStatus, + readBuffer, + TC_MAX_VOLUME_SECTOR_SIZE, + &offset, + NULL); + + if (NT_SUCCESS (ntStatus)) + { + size_t i; + + if (opentest->bDetectTCBootLoader && IoStatus.Information >= TC_SECTOR_SIZE_BIOS) + { + // Search for the string "VeraCrypt" + for (i = 0; i < TC_SECTOR_SIZE_BIOS - strlen (TC_APP_NAME); ++i) + { + if (memcmp (readBuffer + i, TC_APP_NAME, strlen (TC_APP_NAME)) == 0) + { + opentest->TCBootLoaderDetected = TRUE; + break; + } + } + } + + if (opentest->DetectFilesystem && IoStatus.Information >= sizeof (int64)) + { + switch (BE64 (*(uint64 *) readBuffer)) + { + case 0xEB52904E54465320: // NTFS + case 0xEB3C904D53444F53: // FAT16 + case 0xEB58904D53444F53: // FAT32 + case 0xEB76904558464154: // exFAT + + opentest->FilesystemDetected = TRUE; + break; + } + } + } + } + + if (opentest->bMatchVolumeID) + { + int volumeType; + BYTE volumeID[VOLUME_ID_SIZE]; + + // Go through all volume types (e.g., normal, hidden) + for (volumeType = TC_VOLUME_TYPE_NORMAL; + volumeType < TC_VOLUME_TYPE_COUNT; + volumeType++) + { + /* Read the volume header */ + switch (volumeType) + { + case TC_VOLUME_TYPE_NORMAL: + offset.QuadPart = TC_VOLUME_HEADER_OFFSET; + break; + + case TC_VOLUME_TYPE_HIDDEN: + + offset.QuadPart = TC_HIDDEN_VOLUME_HEADER_OFFSET; + break; + } + + ntStatus = ZwReadFile (NtFileHandle, + NULL, + NULL, + NULL, + &IoStatus, + readBuffer, + TC_MAX_VOLUME_SECTOR_SIZE, + &offset, + NULL); + + if (NT_SUCCESS (ntStatus)) + { + /* compute the ID of this volume: SHA-256 of the effective header */ + sha256 (volumeID, readBuffer, TC_VOLUME_HEADER_EFFECTIVE_SIZE); + + if (0 == memcmp (volumeID, opentest->volumeID, VOLUME_ID_SIZE)) + { + opentest->VolumeIDMatched = TRUE; + break; + } + } + } + } + + TCfree (readBuffer); + } + } + + ZwClose (NtFileHandle); + Dump ("Open test on file %ls success.\n", opentest->wszFileName); + } + else + { +#if 0 + Dump ("Open test on file %ls failed NTSTATUS 0x%08x\n", opentest->wszFileName, ntStatus); +#endif + } + + Irp->IoStatus.Information = NT_SUCCESS (ntStatus) ? sizeof (OPEN_TEST_STRUCT) : 0; + Irp->IoStatus.Status = ntStatus; + } + break; + + case TC_IOCTL_GET_SYSTEM_DRIVE_CONFIG: + { + GetSystemDriveConfigurationRequest *request = (GetSystemDriveConfigurationRequest *) Irp->AssociatedIrp.SystemBuffer; + OBJECT_ATTRIBUTES ObjectAttributes; + HANDLE NtFileHandle; + UNICODE_STRING FullFileName; + IO_STATUS_BLOCK IoStatus; + LARGE_INTEGER offset; + byte readBuffer [TC_SECTOR_SIZE_BIOS]; + + if (!ValidateIOBufferSize (Irp, sizeof (GetSystemDriveConfigurationRequest), ValidateInputOutput)) + break; + + EnsureNullTerminatedString (request->DevicePath, sizeof (request->DevicePath)); + RtlInitUnicodeString (&FullFileName, request->DevicePath); + + InitializeObjectAttributes (&ObjectAttributes, &FullFileName, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, NULL, NULL); + + ntStatus = ZwCreateFile (&NtFileHandle, + SYNCHRONIZE | GENERIC_READ, &ObjectAttributes, &IoStatus, NULL, + FILE_ATTRIBUTE_NORMAL, FILE_SHARE_READ | FILE_SHARE_WRITE, FILE_OPEN, FILE_SYNCHRONOUS_IO_NONALERT | FILE_RANDOM_ACCESS, NULL, 0); + + if (NT_SUCCESS (ntStatus)) + { + // Determine if the first sector contains a portion of the VeraCrypt Boot Loader + offset.QuadPart = 0; // MBR + + ntStatus = ZwReadFile (NtFileHandle, + NULL, + NULL, + NULL, + &IoStatus, + readBuffer, + sizeof(readBuffer), + &offset, + NULL); + + if (NT_SUCCESS (ntStatus)) + { + size_t i; + + // Check for dynamic drive + request->DriveIsDynamic = FALSE; + + if (readBuffer[510] == 0x55 && readBuffer[511] == 0xaa) + { + int i; + for (i = 0; i < 4; ++i) + { + if (readBuffer[446 + i * 16 + 4] == PARTITION_LDM) + { + request->DriveIsDynamic = TRUE; + break; + } + } + } + + request->BootLoaderVersion = 0; + request->Configuration = 0; + request->UserConfiguration = 0; + request->CustomUserMessage[0] = 0; + + // Search for the string "VeraCrypt" + for (i = 0; i < sizeof (readBuffer) - strlen (TC_APP_NAME); ++i) + { + if (memcmp (readBuffer + i, TC_APP_NAME, strlen (TC_APP_NAME)) == 0) + { + request->BootLoaderVersion = BE16 (*(uint16 *) (readBuffer + TC_BOOT_SECTOR_VERSION_OFFSET)); + request->Configuration = readBuffer[TC_BOOT_SECTOR_CONFIG_OFFSET]; + + if (request->BootLoaderVersion != 0 && request->BootLoaderVersion <= VERSION_NUM) + { + request->UserConfiguration = readBuffer[TC_BOOT_SECTOR_USER_CONFIG_OFFSET]; + memcpy (request->CustomUserMessage, readBuffer + TC_BOOT_SECTOR_USER_MESSAGE_OFFSET, TC_BOOT_SECTOR_USER_MESSAGE_MAX_LENGTH); + } + break; + } + } + + Irp->IoStatus.Status = STATUS_SUCCESS; + Irp->IoStatus.Information = sizeof (*request); + } + else + { + Irp->IoStatus.Status = ntStatus; + Irp->IoStatus.Information = 0; + } + + ZwClose (NtFileHandle); + + } + else + { + Irp->IoStatus.Status = ntStatus; + Irp->IoStatus.Information = 0; + } + } + break; + + case TC_IOCTL_WIPE_PASSWORD_CACHE: + WipeCache (); + + Irp->IoStatus.Status = STATUS_SUCCESS; + Irp->IoStatus.Information = 0; + break; + + case TC_IOCTL_GET_PASSWORD_CACHE_STATUS: + Irp->IoStatus.Status = cacheEmpty ? STATUS_PIPE_EMPTY : STATUS_SUCCESS; + Irp->IoStatus.Information = 0; + break; + + case TC_IOCTL_SET_PORTABLE_MODE_STATUS: + if (!UserCanAccessDriveDevice()) + { + Irp->IoStatus.Status = STATUS_ACCESS_DENIED; + Irp->IoStatus.Information = 0; + } + else + { + PortableMode = TRUE; + Dump ("Setting portable mode\n"); + } + break; + + case TC_IOCTL_GET_PORTABLE_MODE_STATUS: + Irp->IoStatus.Status = PortableMode ? STATUS_SUCCESS : STATUS_PIPE_EMPTY; + Irp->IoStatus.Information = 0; + break; + + case TC_IOCTL_GET_MOUNTED_VOLUMES: + + if (ValidateIOBufferSize (Irp, sizeof (MOUNT_LIST_STRUCT), ValidateOutput)) + { + MOUNT_LIST_STRUCT *list = (MOUNT_LIST_STRUCT *) Irp->AssociatedIrp.SystemBuffer; + PDEVICE_OBJECT ListDevice; + int drive; + + list->ulMountedDrives = 0; + + for (drive = MIN_MOUNTED_VOLUME_DRIVE_NUMBER; drive <= MAX_MOUNTED_VOLUME_DRIVE_NUMBER; ++drive) + { + PEXTENSION ListExtension; + + ListDevice = GetVirtualVolumeDeviceObject (drive); + if (!ListDevice) + continue; + + ListExtension = (PEXTENSION) ListDevice->DeviceExtension; + if (IsVolumeAccessibleByCurrentUser (ListExtension)) + { + list->ulMountedDrives |= (1 << ListExtension->nDosDriveNo); + RtlStringCbCopyW (list->wszVolume[ListExtension->nDosDriveNo], sizeof(list->wszVolume[ListExtension->nDosDriveNo]),ListExtension->wszVolume); + RtlStringCbCopyW (list->wszLabel[ListExtension->nDosDriveNo], sizeof(list->wszLabel[ListExtension->nDosDriveNo]),ListExtension->wszLabel); + memcpy (list->volumeID[ListExtension->nDosDriveNo], ListExtension->volumeID, VOLUME_ID_SIZE); + list->diskLength[ListExtension->nDosDriveNo] = ListExtension->DiskLength; + list->ea[ListExtension->nDosDriveNo] = ListExtension->cryptoInfo->ea; + if (ListExtension->cryptoInfo->hiddenVolume) + list->volumeType[ListExtension->nDosDriveNo] = PROP_VOL_TYPE_HIDDEN; // Hidden volume + else if (ListExtension->cryptoInfo->bHiddenVolProtectionAction) + list->volumeType[ListExtension->nDosDriveNo] = PROP_VOL_TYPE_OUTER_VOL_WRITE_PREVENTED; // Normal/outer volume (hidden volume protected AND write already prevented) + else if (ListExtension->cryptoInfo->bProtectHiddenVolume) + list->volumeType[ListExtension->nDosDriveNo] = PROP_VOL_TYPE_OUTER; // Normal/outer volume (hidden volume protected) + else + list->volumeType[ListExtension->nDosDriveNo] = PROP_VOL_TYPE_NORMAL; // Normal volume + list->truecryptMode[ListExtension->nDosDriveNo] = ListExtension->cryptoInfo->bTrueCryptMode; + } + } + + Irp->IoStatus.Status = STATUS_SUCCESS; + Irp->IoStatus.Information = sizeof (MOUNT_LIST_STRUCT); + } + break; + + case TC_IOCTL_LEGACY_GET_MOUNTED_VOLUMES: + if (ValidateIOBufferSize (Irp, sizeof (uint32), ValidateOutput)) + { + // Prevent the user from downgrading to versions lower than 5.0 by faking mounted volumes. + // The user could render the system unbootable by downgrading when boot encryption + // is active or being set up. + + memset (Irp->AssociatedIrp.SystemBuffer, 0, irpSp->Parameters.DeviceIoControl.OutputBufferLength); + *(uint32 *) Irp->AssociatedIrp.SystemBuffer = 0xffffFFFF; + + Irp->IoStatus.Status = STATUS_SUCCESS; + Irp->IoStatus.Information = irpSp->Parameters.DeviceIoControl.OutputBufferLength; + } + break; + + case TC_IOCTL_GET_VOLUME_PROPERTIES: + if (ValidateIOBufferSize (Irp, sizeof (VOLUME_PROPERTIES_STRUCT), ValidateInputOutput)) + { + VOLUME_PROPERTIES_STRUCT *prop = (VOLUME_PROPERTIES_STRUCT *) Irp->AssociatedIrp.SystemBuffer; + PDEVICE_OBJECT ListDevice = GetVirtualVolumeDeviceObject (prop->driveNo); + + Irp->IoStatus.Status = STATUS_INVALID_PARAMETER; + Irp->IoStatus.Information = 0; + + if (ListDevice) + { + PEXTENSION ListExtension = (PEXTENSION) ListDevice->DeviceExtension; + if (IsVolumeAccessibleByCurrentUser (ListExtension)) + { + prop->uniqueId = ListExtension->UniqueVolumeId; + RtlStringCbCopyW (prop->wszVolume, sizeof(prop->wszVolume),ListExtension->wszVolume); + RtlStringCbCopyW (prop->wszLabel, sizeof(prop->wszLabel),ListExtension->wszLabel); + memcpy (prop->volumeID, ListExtension->volumeID, VOLUME_ID_SIZE); + prop->bDriverSetLabel = ListExtension->bDriverSetLabel; + prop->diskLength = ListExtension->DiskLength; + prop->ea = ListExtension->cryptoInfo->ea; + prop->mode = ListExtension->cryptoInfo->mode; + prop->pkcs5 = ListExtension->cryptoInfo->pkcs5; + prop->pkcs5Iterations = ListExtension->cryptoInfo->noIterations; + prop->volumePim = ListExtension->cryptoInfo->volumePim; +#if 0 + prop->volumeCreationTime = ListExtension->cryptoInfo->volume_creation_time; + prop->headerCreationTime = ListExtension->cryptoInfo->header_creation_time; +#endif + prop->volumeHeaderFlags = ListExtension->cryptoInfo->HeaderFlags; + prop->readOnly = ListExtension->bReadOnly; + prop->removable = ListExtension->bRemovable; + prop->partitionInInactiveSysEncScope = ListExtension->PartitionInInactiveSysEncScope; + prop->hiddenVolume = ListExtension->cryptoInfo->hiddenVolume; + + if (ListExtension->cryptoInfo->bProtectHiddenVolume) + prop->hiddenVolProtection = ListExtension->cryptoInfo->bHiddenVolProtectionAction ? HIDVOL_PROT_STATUS_ACTION_TAKEN : HIDVOL_PROT_STATUS_ACTIVE; + else + prop->hiddenVolProtection = HIDVOL_PROT_STATUS_NONE; + + prop->totalBytesRead = ListExtension->Queue.TotalBytesRead; + prop->totalBytesWritten = ListExtension->Queue.TotalBytesWritten; + + prop->volFormatVersion = ListExtension->cryptoInfo->LegacyVolume ? TC_VOLUME_FORMAT_VERSION_PRE_6_0 : TC_VOLUME_FORMAT_VERSION; + + Irp->IoStatus.Status = STATUS_SUCCESS; + Irp->IoStatus.Information = sizeof (VOLUME_PROPERTIES_STRUCT); + } + } + } + break; + + case TC_IOCTL_GET_RESOLVED_SYMLINK: + if (ValidateIOBufferSize (Irp, sizeof (RESOLVE_SYMLINK_STRUCT), ValidateInputOutput)) + { + RESOLVE_SYMLINK_STRUCT *resolve = (RESOLVE_SYMLINK_STRUCT *) Irp->AssociatedIrp.SystemBuffer; + { + NTSTATUS ntStatus; + + EnsureNullTerminatedString (resolve->symLinkName, sizeof (resolve->symLinkName)); + + ntStatus = SymbolicLinkToTarget (resolve->symLinkName, + resolve->targetName, + sizeof (resolve->targetName)); + + Irp->IoStatus.Information = sizeof (RESOLVE_SYMLINK_STRUCT); + Irp->IoStatus.Status = ntStatus; + } + } + break; + + case TC_IOCTL_GET_DRIVE_PARTITION_INFO: + if (ValidateIOBufferSize (Irp, sizeof (DISK_PARTITION_INFO_STRUCT), ValidateInputOutput)) + { + DISK_PARTITION_INFO_STRUCT *info = (DISK_PARTITION_INFO_STRUCT *) Irp->AssociatedIrp.SystemBuffer; + { + PARTITION_INFORMATION_EX pi; + NTSTATUS ntStatus; + + EnsureNullTerminatedString (info->deviceName, sizeof (info->deviceName)); + + ntStatus = TCDeviceIoControl (info->deviceName, IOCTL_DISK_GET_PARTITION_INFO_EX, NULL, 0, &pi, sizeof (pi)); + if (NT_SUCCESS(ntStatus)) + { + memset (&info->partInfo, 0, sizeof (info->partInfo)); + + info->partInfo.PartitionLength = pi.PartitionLength; + info->partInfo.PartitionNumber = pi.PartitionNumber; + info->partInfo.StartingOffset = pi.StartingOffset; + + if (pi.PartitionStyle == PARTITION_STYLE_MBR) + { + info->partInfo.PartitionType = pi.Mbr.PartitionType; + info->partInfo.BootIndicator = pi.Mbr.BootIndicator; + } + + info->IsGPT = pi.PartitionStyle == PARTITION_STYLE_GPT; + } + else + { + // Windows 2000 does not support IOCTL_DISK_GET_PARTITION_INFO_EX + ntStatus = TCDeviceIoControl (info->deviceName, IOCTL_DISK_GET_PARTITION_INFO, NULL, 0, &info->partInfo, sizeof (info->partInfo)); + info->IsGPT = FALSE; + } + + if (!NT_SUCCESS (ntStatus)) + { + GET_LENGTH_INFORMATION lengthInfo; + ntStatus = TCDeviceIoControl (info->deviceName, IOCTL_DISK_GET_LENGTH_INFO, NULL, 0, &lengthInfo, sizeof (lengthInfo)); + + if (NT_SUCCESS (ntStatus)) + { + memset (&info->partInfo, 0, sizeof (info->partInfo)); + info->partInfo.PartitionLength = lengthInfo.Length; + } + } + + info->IsDynamic = FALSE; + + if (NT_SUCCESS (ntStatus) && OsMajorVersion >= 6) + { +# define IOCTL_VOLUME_IS_DYNAMIC CTL_CODE(IOCTL_VOLUME_BASE, 18, METHOD_BUFFERED, FILE_ANY_ACCESS) + if (!NT_SUCCESS (TCDeviceIoControl (info->deviceName, IOCTL_VOLUME_IS_DYNAMIC, NULL, 0, &info->IsDynamic, sizeof (info->IsDynamic)))) + info->IsDynamic = FALSE; + } + + Irp->IoStatus.Information = sizeof (DISK_PARTITION_INFO_STRUCT); + Irp->IoStatus.Status = ntStatus; + } + } + break; + + case TC_IOCTL_GET_DRIVE_GEOMETRY: + if (ValidateIOBufferSize (Irp, sizeof (DISK_GEOMETRY_STRUCT), ValidateInputOutput)) + { + DISK_GEOMETRY_STRUCT *g = (DISK_GEOMETRY_STRUCT *) Irp->AssociatedIrp.SystemBuffer; + { + NTSTATUS ntStatus; + + EnsureNullTerminatedString (g->deviceName, sizeof (g->deviceName)); + + ntStatus = TCDeviceIoControl (g->deviceName, + IOCTL_DISK_GET_DRIVE_GEOMETRY, + NULL, 0, &g->diskGeometry, sizeof (g->diskGeometry)); + + Irp->IoStatus.Information = sizeof (DISK_GEOMETRY_STRUCT); + Irp->IoStatus.Status = ntStatus; + } + } + break; + + case TC_IOCTL_PROBE_REAL_DRIVE_SIZE: + if (ValidateIOBufferSize (Irp, sizeof (ProbeRealDriveSizeRequest), ValidateInputOutput)) + { + ProbeRealDriveSizeRequest *request = (ProbeRealDriveSizeRequest *) Irp->AssociatedIrp.SystemBuffer; + NTSTATUS status; + UNICODE_STRING name; + PFILE_OBJECT fileObject; + PDEVICE_OBJECT deviceObject; + + EnsureNullTerminatedString (request->DeviceName, sizeof (request->DeviceName)); + + RtlInitUnicodeString (&name, request->DeviceName); + status = IoGetDeviceObjectPointer (&name, FILE_READ_ATTRIBUTES, &fileObject, &deviceObject); + if (!NT_SUCCESS (status)) + { + Irp->IoStatus.Information = 0; + Irp->IoStatus.Status = status; + break; + } + + status = ProbeRealDriveSize (deviceObject, &request->RealDriveSize); + ObDereferenceObject (fileObject); + + if (status == STATUS_TIMEOUT) + { + request->TimeOut = TRUE; + Irp->IoStatus.Information = sizeof (ProbeRealDriveSizeRequest); + Irp->IoStatus.Status = STATUS_SUCCESS; + } + else if (!NT_SUCCESS (status)) + { + Irp->IoStatus.Information = 0; + Irp->IoStatus.Status = status; + } + else + { + request->TimeOut = FALSE; + Irp->IoStatus.Information = sizeof (ProbeRealDriveSizeRequest); + Irp->IoStatus.Status = status; + } + } + break; + + case TC_IOCTL_MOUNT_VOLUME: + if (ValidateIOBufferSize (Irp, sizeof (MOUNT_STRUCT), ValidateInputOutput)) + { + MOUNT_STRUCT *mount = (MOUNT_STRUCT *) Irp->AssociatedIrp.SystemBuffer; + + if (mount->VolumePassword.Length > MAX_PASSWORD || mount->ProtectedHidVolPassword.Length > MAX_PASSWORD + || mount->pkcs5_prf < 0 || mount->pkcs5_prf > LAST_PRF_ID + || mount->VolumePim < -1 || mount->VolumePim == INT_MAX + || mount->ProtectedHidVolPkcs5Prf < 0 || mount->ProtectedHidVolPkcs5Prf > LAST_PRF_ID + || (mount->bTrueCryptMode != FALSE && mount->bTrueCryptMode != TRUE) + ) + { + Irp->IoStatus.Status = STATUS_INVALID_PARAMETER; + Irp->IoStatus.Information = 0; + break; + } + + EnsureNullTerminatedString (mount->wszVolume, sizeof (mount->wszVolume)); + EnsureNullTerminatedString (mount->wszLabel, sizeof (mount->wszLabel)); + + Irp->IoStatus.Information = sizeof (MOUNT_STRUCT); + Irp->IoStatus.Status = MountDevice (DeviceObject, mount); + + burn (&mount->VolumePassword, sizeof (mount->VolumePassword)); + burn (&mount->ProtectedHidVolPassword, sizeof (mount->ProtectedHidVolPassword)); + burn (&mount->pkcs5_prf, sizeof (mount->pkcs5_prf)); + burn (&mount->VolumePim, sizeof (mount->VolumePim)); + burn (&mount->bTrueCryptMode, sizeof (mount->bTrueCryptMode)); + burn (&mount->ProtectedHidVolPkcs5Prf, sizeof (mount->ProtectedHidVolPkcs5Prf)); + burn (&mount->ProtectedHidVolPim, sizeof (mount->ProtectedHidVolPim)); + } + break; + + case TC_IOCTL_DISMOUNT_VOLUME: + if (ValidateIOBufferSize (Irp, sizeof (UNMOUNT_STRUCT), ValidateInputOutput)) + { + UNMOUNT_STRUCT *unmount = (UNMOUNT_STRUCT *) Irp->AssociatedIrp.SystemBuffer; + PDEVICE_OBJECT ListDevice = GetVirtualVolumeDeviceObject (unmount->nDosDriveNo); + + unmount->nReturnCode = ERR_DRIVE_NOT_FOUND; + + if (ListDevice) + { + PEXTENSION ListExtension = (PEXTENSION) ListDevice->DeviceExtension; + + if (IsVolumeAccessibleByCurrentUser (ListExtension)) + unmount->nReturnCode = UnmountDevice (unmount, ListDevice, unmount->ignoreOpenFiles); + } + + Irp->IoStatus.Information = sizeof (UNMOUNT_STRUCT); + Irp->IoStatus.Status = STATUS_SUCCESS; + } + break; + + case TC_IOCTL_DISMOUNT_ALL_VOLUMES: + if (ValidateIOBufferSize (Irp, sizeof (UNMOUNT_STRUCT), ValidateInputOutput)) + { + UNMOUNT_STRUCT *unmount = (UNMOUNT_STRUCT *) Irp->AssociatedIrp.SystemBuffer; + + unmount->nReturnCode = UnmountAllDevices (unmount, unmount->ignoreOpenFiles); + + Irp->IoStatus.Information = sizeof (UNMOUNT_STRUCT); + Irp->IoStatus.Status = STATUS_SUCCESS; + } + break; + + case TC_IOCTL_BOOT_ENCRYPTION_SETUP: + Irp->IoStatus.Status = StartBootEncryptionSetup (DeviceObject, Irp, irpSp); + Irp->IoStatus.Information = 0; + break; + + case TC_IOCTL_ABORT_BOOT_ENCRYPTION_SETUP: + Irp->IoStatus.Status = AbortBootEncryptionSetup(); + Irp->IoStatus.Information = 0; + break; + + case TC_IOCTL_GET_BOOT_ENCRYPTION_STATUS: + GetBootEncryptionStatus (Irp, irpSp); + break; + + case TC_IOCTL_GET_BOOT_ENCRYPTION_SETUP_RESULT: + Irp->IoStatus.Information = 0; + Irp->IoStatus.Status = GetSetupResult(); + break; + + case TC_IOCTL_GET_BOOT_DRIVE_VOLUME_PROPERTIES: + GetBootDriveVolumeProperties (Irp, irpSp); + break; + + case TC_IOCTL_GET_BOOT_LOADER_VERSION: + GetBootLoaderVersion (Irp, irpSp); + break; + + case TC_IOCTL_REOPEN_BOOT_VOLUME_HEADER: + ReopenBootVolumeHeader (Irp, irpSp); + break; + + case VC_IOCTL_GET_BOOT_LOADER_FINGERPRINT: + GetBootLoaderFingerprint (Irp, irpSp); + break; + + case TC_IOCTL_GET_BOOT_ENCRYPTION_ALGORITHM_NAME: + GetBootEncryptionAlgorithmName (Irp, irpSp); + break; + + case TC_IOCTL_IS_HIDDEN_SYSTEM_RUNNING: + if (ValidateIOBufferSize (Irp, sizeof (int), ValidateOutput)) + { + *(int *) Irp->AssociatedIrp.SystemBuffer = IsHiddenSystemRunning() ? 1 : 0; + Irp->IoStatus.Information = sizeof (int); + Irp->IoStatus.Status = STATUS_SUCCESS; + } + break; + + case TC_IOCTL_START_DECOY_SYSTEM_WIPE: + Irp->IoStatus.Status = StartDecoySystemWipe (DeviceObject, Irp, irpSp); + Irp->IoStatus.Information = 0; + break; + + case TC_IOCTL_ABORT_DECOY_SYSTEM_WIPE: + Irp->IoStatus.Status = AbortDecoySystemWipe(); + Irp->IoStatus.Information = 0; + break; + + case TC_IOCTL_GET_DECOY_SYSTEM_WIPE_RESULT: + Irp->IoStatus.Status = GetDecoySystemWipeResult(); + Irp->IoStatus.Information = 0; + break; + + case TC_IOCTL_GET_DECOY_SYSTEM_WIPE_STATUS: + GetDecoySystemWipeStatus (Irp, irpSp); + break; + + case TC_IOCTL_WRITE_BOOT_DRIVE_SECTOR: + Irp->IoStatus.Status = WriteBootDriveSector (Irp, irpSp); + Irp->IoStatus.Information = 0; + break; + + case TC_IOCTL_GET_WARNING_FLAGS: + if (ValidateIOBufferSize (Irp, sizeof (GetWarningFlagsRequest), ValidateOutput)) + { + GetWarningFlagsRequest *flags = (GetWarningFlagsRequest *) Irp->AssociatedIrp.SystemBuffer; + + flags->PagingFileCreationPrevented = PagingFileCreationPrevented; + PagingFileCreationPrevented = FALSE; + flags->SystemFavoriteVolumeDirty = SystemFavoriteVolumeDirty; + SystemFavoriteVolumeDirty = FALSE; + + Irp->IoStatus.Information = sizeof (GetWarningFlagsRequest); + Irp->IoStatus.Status = STATUS_SUCCESS; + } + break; + + case TC_IOCTL_SET_SYSTEM_FAVORITE_VOLUME_DIRTY: + if (UserCanAccessDriveDevice()) + { + SystemFavoriteVolumeDirty = TRUE; + Irp->IoStatus.Status = STATUS_SUCCESS; + } + else + Irp->IoStatus.Status = STATUS_ACCESS_DENIED; + + Irp->IoStatus.Information = 0; + break; + + case TC_IOCTL_REREAD_DRIVER_CONFIG: + Irp->IoStatus.Status = ReadRegistryConfigFlags (FALSE); + Irp->IoStatus.Information = 0; + break; + + case TC_IOCTL_GET_SYSTEM_DRIVE_DUMP_CONFIG: + if ( (ValidateIOBufferSize (Irp, sizeof (GetSystemDriveDumpConfigRequest), ValidateOutput)) + && (Irp->RequestorMode == KernelMode) + ) + { + GetSystemDriveDumpConfigRequest *request = (GetSystemDriveDumpConfigRequest *) Irp->AssociatedIrp.SystemBuffer; + + request->BootDriveFilterExtension = GetBootDriveFilterExtension(); + if (IsBootDriveMounted() && request->BootDriveFilterExtension) + { + request->HwEncryptionEnabled = IsHwEncryptionEnabled(); + Irp->IoStatus.Status = STATUS_SUCCESS; + Irp->IoStatus.Information = sizeof (*request); + } + else + { + Irp->IoStatus.Status = STATUS_INVALID_PARAMETER; + Irp->IoStatus.Information = 0; + } + } + break; + + default: + return TCCompleteIrp (Irp, STATUS_INVALID_DEVICE_REQUEST, 0); + } + + +#ifdef DEBUG + if (!NT_SUCCESS (Irp->IoStatus.Status)) + { + switch (irpSp->Parameters.DeviceIoControl.IoControlCode) + { + case TC_IOCTL_GET_MOUNTED_VOLUMES: + case TC_IOCTL_GET_PASSWORD_CACHE_STATUS: + case TC_IOCTL_GET_PORTABLE_MODE_STATUS: + case TC_IOCTL_SET_PORTABLE_MODE_STATUS: + case TC_IOCTL_OPEN_TEST: + case TC_IOCTL_GET_RESOLVED_SYMLINK: + case TC_IOCTL_GET_DRIVE_PARTITION_INFO: + case TC_IOCTL_GET_BOOT_DRIVE_VOLUME_PROPERTIES: + case TC_IOCTL_GET_BOOT_ENCRYPTION_STATUS: + case TC_IOCTL_IS_HIDDEN_SYSTEM_RUNNING: + break; + + default: + Dump ("IOCTL error 0x%08x\n", Irp->IoStatus.Status); + } + } +#endif + + return TCCompleteIrp (Irp, Irp->IoStatus.Status, Irp->IoStatus.Information); +} + + +NTSTATUS TCStartThread (PKSTART_ROUTINE threadProc, PVOID threadArg, PKTHREAD *kThread) +{ + return TCStartThreadInProcess (threadProc, threadArg, kThread, NULL); +} + + +NTSTATUS TCStartThreadInProcess (PKSTART_ROUTINE threadProc, PVOID threadArg, PKTHREAD *kThread, PEPROCESS process) +{ + NTSTATUS status; + HANDLE threadHandle; + HANDLE processHandle = NULL; + OBJECT_ATTRIBUTES threadObjAttributes; + + if (process) + { + status = ObOpenObjectByPointer (process, OBJ_KERNEL_HANDLE, NULL, 0, NULL, KernelMode, &processHandle); + if (!NT_SUCCESS (status)) + return status; + } + + InitializeObjectAttributes (&threadObjAttributes, NULL, OBJ_KERNEL_HANDLE, NULL, NULL); + + status = PsCreateSystemThread (&threadHandle, THREAD_ALL_ACCESS, &threadObjAttributes, processHandle, NULL, threadProc, threadArg); + if (!NT_SUCCESS (status)) + return status; + + status = ObReferenceObjectByHandle (threadHandle, THREAD_ALL_ACCESS, NULL, KernelMode, (PVOID *) kThread, NULL); + if (!NT_SUCCESS (status)) + { + ZwClose (threadHandle); + *kThread = NULL; + return status; + } + + if (processHandle) + ZwClose (processHandle); + + ZwClose (threadHandle); + return STATUS_SUCCESS; +} + + +void TCStopThread (PKTHREAD kThread, PKEVENT wakeUpEvent) +{ + if (wakeUpEvent) + KeSetEvent (wakeUpEvent, 0, FALSE); + + KeWaitForSingleObject (kThread, Executive, KernelMode, FALSE, NULL); + ObDereferenceObject (kThread); +} + + +NTSTATUS TCStartVolumeThread (PDEVICE_OBJECT DeviceObject, PEXTENSION Extension, MOUNT_STRUCT * mount) +{ + PTHREAD_BLOCK pThreadBlock = TCalloc (sizeof (THREAD_BLOCK)); + HANDLE hThread; + NTSTATUS ntStatus; + OBJECT_ATTRIBUTES threadObjAttributes; + SECURITY_QUALITY_OF_SERVICE qos; + + Dump ("Starting thread...\n"); + + if (pThreadBlock == NULL) + { + return STATUS_INSUFFICIENT_RESOURCES; + } + else + { + pThreadBlock->DeviceObject = DeviceObject; + pThreadBlock->mount = mount; + } + + qos.Length = sizeof (qos); + qos.ContextTrackingMode = SECURITY_STATIC_TRACKING; + qos.EffectiveOnly = TRUE; + qos.ImpersonationLevel = SecurityImpersonation; + + ntStatus = SeCreateClientSecurity (PsGetCurrentThread(), &qos, FALSE, &Extension->SecurityClientContext); + if (!NT_SUCCESS (ntStatus)) + goto ret; + + Extension->SecurityClientContextValid = TRUE; + + Extension->bThreadShouldQuit = FALSE; + + InitializeObjectAttributes (&threadObjAttributes, NULL, OBJ_KERNEL_HANDLE, NULL, NULL); + + ntStatus = PsCreateSystemThread (&hThread, + THREAD_ALL_ACCESS, + &threadObjAttributes, + NULL, + NULL, + VolumeThreadProc, + pThreadBlock); + + if (!NT_SUCCESS (ntStatus)) + { + Dump ("PsCreateSystemThread Failed END\n"); + goto ret; + } + + ntStatus = ObReferenceObjectByHandle (hThread, + THREAD_ALL_ACCESS, + NULL, + KernelMode, + &Extension->peThread, + NULL); + + ZwClose (hThread); + + if (!NT_SUCCESS (ntStatus)) + goto ret; + + Dump ("Waiting for thread to initialize...\n"); + + KeWaitForSingleObject (&Extension->keCreateEvent, + Executive, + KernelMode, + FALSE, + NULL); + + Dump ("Waiting completed! Thread returns 0x%08x\n", pThreadBlock->ntCreateStatus); + ntStatus = pThreadBlock->ntCreateStatus; + +ret: + TCfree (pThreadBlock); + return ntStatus; +} + +void TCStopVolumeThread (PDEVICE_OBJECT DeviceObject, PEXTENSION Extension) +{ + NTSTATUS ntStatus; + + UNREFERENCED_PARAMETER (DeviceObject); /* Remove compiler warning */ + + Dump ("Signalling thread to quit...\n"); + + Extension->bThreadShouldQuit = TRUE; + + KeReleaseSemaphore (&Extension->RequestSemaphore, + 0, + 1, + TRUE); + + ntStatus = KeWaitForSingleObject (Extension->peThread, + Executive, + KernelMode, + FALSE, + NULL); + + ASSERT (NT_SUCCESS (ntStatus)); + + ObDereferenceObject (Extension->peThread); + Extension->peThread = NULL; + + Dump ("Thread exited\n"); +} + + +// Suspend current thread for a number of milliseconds +void TCSleep (int milliSeconds) +{ + PKTIMER timer = (PKTIMER) TCalloc (sizeof (KTIMER)); + LARGE_INTEGER duetime; + + if (!timer) + return; + + duetime.QuadPart = (__int64) milliSeconds * -10000; + KeInitializeTimerEx(timer, NotificationTimer); + KeSetTimerEx(timer, duetime, 0, NULL); + + KeWaitForSingleObject (timer, Executive, KernelMode, FALSE, NULL); + + TCfree (timer); +} + +BOOL IsDeviceName(wchar_t wszVolume[TC_MAX_PATH]) +{ + if ( (wszVolume[0] == '\\') + && (wszVolume[1] == 'D' || wszVolume[1] == 'd') + && (wszVolume[2] == 'E' || wszVolume[2] == 'e') + && (wszVolume[3] == 'V' || wszVolume[3] == 'v') + && (wszVolume[4] == 'I' || wszVolume[4] == 'i') + && (wszVolume[5] == 'C' || wszVolume[5] == 'c') + && (wszVolume[6] == 'E' || wszVolume[6] == 'e') + ) + { + return TRUE; + } + else + return FALSE; +} + +/* VolumeThreadProc does all the work of processing IRP's, and dispatching them + to either the ReadWrite function or the DeviceControl function */ +VOID VolumeThreadProc (PVOID Context) +{ + PTHREAD_BLOCK pThreadBlock = (PTHREAD_BLOCK) Context; + PDEVICE_OBJECT DeviceObject = pThreadBlock->DeviceObject; + PEXTENSION Extension = (PEXTENSION) DeviceObject->DeviceExtension; + BOOL bDevice; + + /* Set thread priority to lowest realtime level. */ + KeSetPriorityThread (KeGetCurrentThread (), LOW_REALTIME_PRIORITY); + + Dump ("Mount THREAD OPENING VOLUME BEGIN\n"); + + if ( !IsDeviceName (pThreadBlock->mount->wszVolume)) + { + RtlStringCbCopyW (pThreadBlock->wszMountVolume, sizeof(pThreadBlock->wszMountVolume),WIDE ("\\??\\")); + RtlStringCbCatW (pThreadBlock->wszMountVolume, sizeof(pThreadBlock->wszMountVolume),pThreadBlock->mount->wszVolume); + bDevice = FALSE; + } + else + { + pThreadBlock->wszMountVolume[0] = 0; + RtlStringCbCatW (pThreadBlock->wszMountVolume, sizeof(pThreadBlock->wszMountVolume),pThreadBlock->mount->wszVolume); + bDevice = TRUE; + } + + Dump ("Mount THREAD request for File %ls DriveNumber %d Device = %d\n", + pThreadBlock->wszMountVolume, pThreadBlock->mount->nDosDriveNo, bDevice); + + pThreadBlock->ntCreateStatus = TCOpenVolume (DeviceObject, + Extension, + pThreadBlock->mount, + pThreadBlock->wszMountVolume, + bDevice); + + if (!NT_SUCCESS (pThreadBlock->ntCreateStatus) || pThreadBlock->mount->nReturnCode != 0) + { + KeSetEvent (&Extension->keCreateEvent, 0, FALSE); + PsTerminateSystemThread (STATUS_SUCCESS); + } + + // Start IO queue + Extension->Queue.IsFilterDevice = FALSE; + Extension->Queue.DeviceObject = DeviceObject; + Extension->Queue.CryptoInfo = Extension->cryptoInfo; + Extension->Queue.HostFileHandle = Extension->hDeviceFile; + Extension->Queue.VirtualDeviceLength = Extension->DiskLength; + Extension->Queue.MaxReadAheadOffset.QuadPart = Extension->HostLength; + + if (Extension->SecurityClientContextValid) + Extension->Queue.SecurityClientContext = &Extension->SecurityClientContext; + else + Extension->Queue.SecurityClientContext = NULL; + + pThreadBlock->ntCreateStatus = EncryptedIoQueueStart (&Extension->Queue); + + if (!NT_SUCCESS (pThreadBlock->ntCreateStatus)) + { + TCCloseVolume (DeviceObject, Extension); + + pThreadBlock->mount->nReturnCode = ERR_OS_ERROR; + KeSetEvent (&Extension->keCreateEvent, 0, FALSE); + PsTerminateSystemThread (STATUS_SUCCESS); + } + + KeSetEvent (&Extension->keCreateEvent, 0, FALSE); + /* From this point on pThreadBlock cannot be used as it will have been released! */ + pThreadBlock = NULL; + + for (;;) + { + /* Wait for a request from the dispatch routines. */ + KeWaitForSingleObject ((PVOID) & Extension->RequestSemaphore, Executive, KernelMode, FALSE, NULL); + + for (;;) + { + PIO_STACK_LOCATION irpSp; + PLIST_ENTRY request; + PIRP irp; + + request = ExInterlockedRemoveHeadList (&Extension->ListEntry, &Extension->ListSpinLock); + if (request == NULL) + break; + + irp = CONTAINING_RECORD (request, IRP, Tail.Overlay.ListEntry); + irpSp = IoGetCurrentIrpStackLocation (irp); + + ASSERT (irpSp->MajorFunction == IRP_MJ_DEVICE_CONTROL); + + ProcessVolumeDeviceControlIrp (DeviceObject, Extension, irp); + IoReleaseRemoveLock (&Extension->Queue.RemoveLock, irp); + } + + if (Extension->bThreadShouldQuit) + { + Dump ("Closing volume\n"); + EncryptedIoQueueStop (&Extension->Queue); + + TCCloseVolume (DeviceObject, Extension); + PsTerminateSystemThread (STATUS_SUCCESS); + } + } +} + +void TCGetNTNameFromNumber (LPWSTR ntname, int cbNtName, int nDriveNo) +{ + WCHAR tmp[2] = + {0, 0}; + int j = nDriveNo + (WCHAR) 'A'; + + tmp[0] = (short) j; + RtlStringCbCopyW (ntname, cbNtName,(LPWSTR) NT_MOUNT_PREFIX); + RtlStringCbCatW (ntname, cbNtName, tmp); +} + +void TCGetDosNameFromNumber (LPWSTR dosname,int cbDosName, int nDriveNo, DeviceNamespaceType namespaceType) +{ + WCHAR tmp[3] = + {0, ':', 0}; + int j = nDriveNo + (WCHAR) 'A'; + + tmp[0] = (short) j; + + if (DeviceNamespaceGlobal == namespaceType) + { + RtlStringCbCopyW (dosname, cbDosName, (LPWSTR) DOS_MOUNT_PREFIX_GLOBAL); + } + else + { + RtlStringCbCopyW (dosname, cbDosName, (LPWSTR) DOS_MOUNT_PREFIX_DEFAULT); + } + + RtlStringCbCatW (dosname, cbDosName, tmp); +} + +#ifdef _DEBUG +LPWSTR TCTranslateCode (ULONG ulCode) +{ + switch (ulCode) + { +#define TC_CASE_RET_NAME(CODE) case CODE : return L###CODE + + TC_CASE_RET_NAME (TC_IOCTL_ABORT_BOOT_ENCRYPTION_SETUP); + TC_CASE_RET_NAME (TC_IOCTL_ABORT_DECOY_SYSTEM_WIPE); + TC_CASE_RET_NAME (TC_IOCTL_BOOT_ENCRYPTION_SETUP); + TC_CASE_RET_NAME (TC_IOCTL_DISMOUNT_ALL_VOLUMES); + TC_CASE_RET_NAME (TC_IOCTL_DISMOUNT_VOLUME); + TC_CASE_RET_NAME (TC_IOCTL_GET_BOOT_DRIVE_VOLUME_PROPERTIES); + TC_CASE_RET_NAME (TC_IOCTL_GET_BOOT_ENCRYPTION_ALGORITHM_NAME); + TC_CASE_RET_NAME (TC_IOCTL_GET_BOOT_ENCRYPTION_SETUP_RESULT); + TC_CASE_RET_NAME (TC_IOCTL_GET_BOOT_ENCRYPTION_STATUS); + TC_CASE_RET_NAME (TC_IOCTL_GET_BOOT_LOADER_VERSION); + TC_CASE_RET_NAME (TC_IOCTL_GET_DECOY_SYSTEM_WIPE_RESULT); + TC_CASE_RET_NAME (TC_IOCTL_GET_DECOY_SYSTEM_WIPE_STATUS); + TC_CASE_RET_NAME (TC_IOCTL_GET_DEVICE_REFCOUNT); + TC_CASE_RET_NAME (TC_IOCTL_GET_DRIVE_GEOMETRY); + TC_CASE_RET_NAME (TC_IOCTL_GET_DRIVE_PARTITION_INFO); + TC_CASE_RET_NAME (TC_IOCTL_GET_DRIVER_VERSION); + TC_CASE_RET_NAME (TC_IOCTL_GET_MOUNTED_VOLUMES); + TC_CASE_RET_NAME (TC_IOCTL_GET_PASSWORD_CACHE_STATUS); + TC_CASE_RET_NAME (TC_IOCTL_GET_SYSTEM_DRIVE_CONFIG); + TC_CASE_RET_NAME (TC_IOCTL_GET_PORTABLE_MODE_STATUS); + TC_CASE_RET_NAME (TC_IOCTL_SET_PORTABLE_MODE_STATUS); + TC_CASE_RET_NAME (TC_IOCTL_GET_RESOLVED_SYMLINK); + TC_CASE_RET_NAME (TC_IOCTL_GET_SYSTEM_DRIVE_DUMP_CONFIG); + TC_CASE_RET_NAME (TC_IOCTL_GET_VOLUME_PROPERTIES); + TC_CASE_RET_NAME (TC_IOCTL_GET_WARNING_FLAGS); + TC_CASE_RET_NAME (TC_IOCTL_DISK_IS_WRITABLE); + TC_CASE_RET_NAME (TC_IOCTL_IS_ANY_VOLUME_MOUNTED); + TC_CASE_RET_NAME (TC_IOCTL_IS_DRIVER_UNLOAD_DISABLED); + TC_CASE_RET_NAME (TC_IOCTL_IS_HIDDEN_SYSTEM_RUNNING); + TC_CASE_RET_NAME (TC_IOCTL_MOUNT_VOLUME); + TC_CASE_RET_NAME (TC_IOCTL_OPEN_TEST); + TC_CASE_RET_NAME (TC_IOCTL_PROBE_REAL_DRIVE_SIZE); + TC_CASE_RET_NAME (TC_IOCTL_REOPEN_BOOT_VOLUME_HEADER); + TC_CASE_RET_NAME (TC_IOCTL_REREAD_DRIVER_CONFIG); + TC_CASE_RET_NAME (TC_IOCTL_SET_SYSTEM_FAVORITE_VOLUME_DIRTY); + TC_CASE_RET_NAME (TC_IOCTL_START_DECOY_SYSTEM_WIPE); + TC_CASE_RET_NAME (TC_IOCTL_WIPE_PASSWORD_CACHE); + TC_CASE_RET_NAME (TC_IOCTL_WRITE_BOOT_DRIVE_SECTOR); + + TC_CASE_RET_NAME (IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS); + +#undef TC_CASE_RET_NAME + } + + if (ulCode == IOCTL_DISK_GET_DRIVE_GEOMETRY) + return (LPWSTR) _T ("IOCTL_DISK_GET_DRIVE_GEOMETRY"); + else if (ulCode == IOCTL_DISK_GET_DRIVE_GEOMETRY_EX) + return (LPWSTR) _T ("IOCTL_DISK_GET_DRIVE_GEOMETRY_EX"); + else if (ulCode == IOCTL_MOUNTDEV_QUERY_DEVICE_NAME) + return (LPWSTR) _T ("IOCTL_MOUNTDEV_QUERY_DEVICE_NAME"); + else if (ulCode == IOCTL_MOUNTDEV_QUERY_SUGGESTED_LINK_NAME) + return (LPWSTR) _T ("IOCTL_MOUNTDEV_QUERY_SUGGESTED_LINK_NAME"); + else if (ulCode == IOCTL_MOUNTDEV_QUERY_UNIQUE_ID) + return (LPWSTR) _T ("IOCTL_MOUNTDEV_QUERY_UNIQUE_ID"); + else if (ulCode == IOCTL_VOLUME_ONLINE) + return (LPWSTR) _T ("IOCTL_VOLUME_ONLINE"); + else if (ulCode == IOCTL_MOUNTDEV_LINK_CREATED) + return (LPWSTR) _T ("IOCTL_MOUNTDEV_LINK_CREATED"); + else if (ulCode == IOCTL_MOUNTDEV_LINK_DELETED) + return (LPWSTR) _T ("IOCTL_MOUNTDEV_LINK_DELETED"); + else if (ulCode == IOCTL_MOUNTMGR_QUERY_POINTS) + return (LPWSTR) _T ("IOCTL_MOUNTMGR_QUERY_POINTS"); + else if (ulCode == IOCTL_MOUNTMGR_VOLUME_MOUNT_POINT_CREATED) + return (LPWSTR) _T ("IOCTL_MOUNTMGR_VOLUME_MOUNT_POINT_CREATED"); + else if (ulCode == IOCTL_MOUNTMGR_VOLUME_MOUNT_POINT_DELETED) + return (LPWSTR) _T ("IOCTL_MOUNTMGR_VOLUME_MOUNT_POINT_DELETED"); + else if (ulCode == IOCTL_DISK_GET_LENGTH_INFO) + return (LPWSTR) _T ("IOCTL_DISK_GET_LENGTH_INFO"); + else if (ulCode == IOCTL_STORAGE_GET_DEVICE_NUMBER) + return (LPWSTR) _T ("IOCTL_STORAGE_GET_DEVICE_NUMBER"); + else if (ulCode == IOCTL_DISK_GET_PARTITION_INFO) + return (LPWSTR) _T ("IOCTL_DISK_GET_PARTITION_INFO"); + else if (ulCode == IOCTL_DISK_GET_PARTITION_INFO_EX) + return (LPWSTR) _T ("IOCTL_DISK_GET_PARTITION_INFO_EX"); + else if (ulCode == IOCTL_DISK_SET_PARTITION_INFO) + return (LPWSTR) _T ("IOCTL_DISK_SET_PARTITION_INFO"); + else if (ulCode == IOCTL_DISK_GET_DRIVE_LAYOUT) + return (LPWSTR) _T ("IOCTL_DISK_GET_DRIVE_LAYOUT"); + else if (ulCode == IOCTL_DISK_SET_DRIVE_LAYOUT_EX) + return (LPWSTR) _T ("IOCTL_DISK_SET_DRIVE_LAYOUT_EX"); + else if (ulCode == IOCTL_DISK_VERIFY) + return (LPWSTR) _T ("IOCTL_DISK_VERIFY"); + else if (ulCode == IOCTL_DISK_FORMAT_TRACKS) + return (LPWSTR) _T ("IOCTL_DISK_FORMAT_TRACKS"); + else if (ulCode == IOCTL_DISK_REASSIGN_BLOCKS) + return (LPWSTR) _T ("IOCTL_DISK_REASSIGN_BLOCKS"); + else if (ulCode == IOCTL_DISK_PERFORMANCE) + return (LPWSTR) _T ("IOCTL_DISK_PERFORMANCE"); + else if (ulCode == IOCTL_DISK_IS_WRITABLE) + return (LPWSTR) _T ("IOCTL_DISK_IS_WRITABLE"); + else if (ulCode == IOCTL_DISK_LOGGING) + return (LPWSTR) _T ("IOCTL_DISK_LOGGING"); + else if (ulCode == IOCTL_DISK_FORMAT_TRACKS_EX) + return (LPWSTR) _T ("IOCTL_DISK_FORMAT_TRACKS_EX"); + else if (ulCode == IOCTL_DISK_HISTOGRAM_STRUCTURE) + return (LPWSTR) _T ("IOCTL_DISK_HISTOGRAM_STRUCTURE"); + else if (ulCode == IOCTL_DISK_HISTOGRAM_DATA) + return (LPWSTR) _T ("IOCTL_DISK_HISTOGRAM_DATA"); + else if (ulCode == IOCTL_DISK_HISTOGRAM_RESET) + return (LPWSTR) _T ("IOCTL_DISK_HISTOGRAM_RESET"); + else if (ulCode == IOCTL_DISK_REQUEST_STRUCTURE) + return (LPWSTR) _T ("IOCTL_DISK_REQUEST_STRUCTURE"); + else if (ulCode == IOCTL_DISK_REQUEST_DATA) + return (LPWSTR) _T ("IOCTL_DISK_REQUEST_DATA"); + else if (ulCode == IOCTL_DISK_CONTROLLER_NUMBER) + return (LPWSTR) _T ("IOCTL_DISK_CONTROLLER_NUMBER"); + else if (ulCode == SMART_GET_VERSION) + return (LPWSTR) _T ("SMART_GET_VERSION"); + else if (ulCode == SMART_SEND_DRIVE_COMMAND) + return (LPWSTR) _T ("SMART_SEND_DRIVE_COMMAND"); + else if (ulCode == SMART_RCV_DRIVE_DATA) + return (LPWSTR) _T ("SMART_RCV_DRIVE_DATA"); + else if (ulCode == IOCTL_DISK_INTERNAL_SET_VERIFY) + return (LPWSTR) _T ("IOCTL_DISK_INTERNAL_SET_VERIFY"); + else if (ulCode == IOCTL_DISK_INTERNAL_CLEAR_VERIFY) + return (LPWSTR) _T ("IOCTL_DISK_INTERNAL_CLEAR_VERIFY"); + else if (ulCode == IOCTL_DISK_CHECK_VERIFY) + return (LPWSTR) _T ("IOCTL_DISK_CHECK_VERIFY"); + else if (ulCode == IOCTL_DISK_MEDIA_REMOVAL) + return (LPWSTR) _T ("IOCTL_DISK_MEDIA_REMOVAL"); + else if (ulCode == IOCTL_DISK_EJECT_MEDIA) + return (LPWSTR) _T ("IOCTL_DISK_EJECT_MEDIA"); + else if (ulCode == IOCTL_DISK_LOAD_MEDIA) + return (LPWSTR) _T ("IOCTL_DISK_LOAD_MEDIA"); + else if (ulCode == IOCTL_DISK_RESERVE) + return (LPWSTR) _T ("IOCTL_DISK_RESERVE"); + else if (ulCode == IOCTL_DISK_RELEASE) + return (LPWSTR) _T ("IOCTL_DISK_RELEASE"); + else if (ulCode == IOCTL_DISK_FIND_NEW_DEVICES) + return (LPWSTR) _T ("IOCTL_DISK_FIND_NEW_DEVICES"); + else if (ulCode == IOCTL_DISK_GET_MEDIA_TYPES) + return (LPWSTR) _T ("IOCTL_DISK_GET_MEDIA_TYPES"); + else if (ulCode == IOCTL_STORAGE_SET_HOTPLUG_INFO) + return (LPWSTR) _T ("IOCTL_STORAGE_SET_HOTPLUG_INFO"); + else if (ulCode == IRP_MJ_READ) + return (LPWSTR) _T ("IRP_MJ_READ"); + else if (ulCode == IRP_MJ_WRITE) + return (LPWSTR) _T ("IRP_MJ_WRITE"); + else if (ulCode == IRP_MJ_CREATE) + return (LPWSTR) _T ("IRP_MJ_CREATE"); + else if (ulCode == IRP_MJ_CLOSE) + return (LPWSTR) _T ("IRP_MJ_CLOSE"); + else if (ulCode == IRP_MJ_CLEANUP) + return (LPWSTR) _T ("IRP_MJ_CLEANUP"); + else if (ulCode == IRP_MJ_FLUSH_BUFFERS) + return (LPWSTR) _T ("IRP_MJ_FLUSH_BUFFERS"); + else if (ulCode == IRP_MJ_SHUTDOWN) + return (LPWSTR) _T ("IRP_MJ_SHUTDOWN"); + else if (ulCode == IRP_MJ_DEVICE_CONTROL) + return (LPWSTR) _T ("IRP_MJ_DEVICE_CONTROL"); + else + { + return (LPWSTR) _T ("IOCTL"); + } +} + +#endif + +void TCDeleteDeviceObject (PDEVICE_OBJECT DeviceObject, PEXTENSION Extension) +{ + UNICODE_STRING Win32NameString; + NTSTATUS ntStatus; + + Dump ("TCDeleteDeviceObject BEGIN\n"); + + if (Extension->bRootDevice) + { + RtlInitUnicodeString (&Win32NameString, (LPWSTR) DOS_ROOT_PREFIX); + ntStatus = IoDeleteSymbolicLink (&Win32NameString); + if (!NT_SUCCESS (ntStatus)) + Dump ("IoDeleteSymbolicLink failed ntStatus = 0x%08x\n", ntStatus); + + RootDeviceObject = NULL; + } + else + { + if (Extension->peThread != NULL) + TCStopVolumeThread (DeviceObject, Extension); + + if (Extension->UserSid) + TCfree (Extension->UserSid); + + if (Extension->SecurityClientContextValid) + { + if (OsMajorVersion == 5 && OsMinorVersion == 0) + { + ObDereferenceObject (Extension->SecurityClientContext.ClientToken); + } + else + { + // Windows 2000 does not support PsDereferenceImpersonationToken() used by SeDeleteClientSecurity(). + // TODO: Use only SeDeleteClientSecurity() once support for Windows 2000 is dropped. + + VOID (*PsDereferenceImpersonationTokenD) (PACCESS_TOKEN ImpersonationToken); + UNICODE_STRING name; + RtlInitUnicodeString (&name, L"PsDereferenceImpersonationToken"); + + PsDereferenceImpersonationTokenD = MmGetSystemRoutineAddress (&name); + if (!PsDereferenceImpersonationTokenD) + TC_BUG_CHECK (STATUS_NOT_IMPLEMENTED); + +# define PsDereferencePrimaryToken +# define PsDereferenceImpersonationToken PsDereferenceImpersonationTokenD + + SeDeleteClientSecurity (&Extension->SecurityClientContext); + +# undef PsDereferencePrimaryToken +# undef PsDereferenceImpersonationToken + } + } + + VirtualVolumeDeviceObjects[Extension->nDosDriveNo] = NULL; + } + + IoDeleteDevice (DeviceObject); + + Dump ("TCDeleteDeviceObject END\n"); +} + + +VOID TCUnloadDriver (PDRIVER_OBJECT DriverObject) +{ + Dump ("TCUnloadDriver BEGIN\n"); + + OnShutdownPending(); + + if (IsBootDriveMounted()) + TC_BUG_CHECK (STATUS_INVALID_DEVICE_STATE); + + EncryptionThreadPoolStop(); + TCDeleteDeviceObject (RootDeviceObject, (PEXTENSION) RootDeviceObject->DeviceExtension); + + Dump ("TCUnloadDriver END\n"); +} + + +void OnShutdownPending () +{ + UNMOUNT_STRUCT unmount; + memset (&unmount, 0, sizeof (unmount)); + unmount.ignoreOpenFiles = TRUE; + + while (SendDeviceIoControlRequest (RootDeviceObject, TC_IOCTL_DISMOUNT_ALL_VOLUMES, &unmount, sizeof (unmount), &unmount, sizeof (unmount)) == STATUS_INSUFFICIENT_RESOURCES || unmount.HiddenVolumeProtectionTriggered) + unmount.HiddenVolumeProtectionTriggered = FALSE; + + while (SendDeviceIoControlRequest (RootDeviceObject, TC_IOCTL_WIPE_PASSWORD_CACHE, NULL, 0, NULL, 0) == STATUS_INSUFFICIENT_RESOURCES); +} + + +NTSTATUS TCDeviceIoControl (PWSTR deviceName, ULONG IoControlCode, void *InputBuffer, ULONG InputBufferSize, void *OutputBuffer, ULONG OutputBufferSize) +{ + IO_STATUS_BLOCK ioStatusBlock; + NTSTATUS ntStatus; + PIRP irp; + PFILE_OBJECT fileObject; + PDEVICE_OBJECT deviceObject; + KEVENT event; + UNICODE_STRING name; + + RtlInitUnicodeString(&name, deviceName); + ntStatus = IoGetDeviceObjectPointer (&name, FILE_READ_ATTRIBUTES, &fileObject, &deviceObject); + + if (!NT_SUCCESS (ntStatus)) + return ntStatus; + + KeInitializeEvent(&event, NotificationEvent, FALSE); + + irp = IoBuildDeviceIoControlRequest (IoControlCode, + deviceObject, + InputBuffer, InputBufferSize, + OutputBuffer, OutputBufferSize, + FALSE, + &event, + &ioStatusBlock); + + if (irp == NULL) + { + Dump ("IRP allocation failed\n"); + ntStatus = STATUS_INSUFFICIENT_RESOURCES; + goto ret; + } + + IoGetNextIrpStackLocation (irp)->FileObject = fileObject; + + ntStatus = IoCallDriver (deviceObject, irp); + if (ntStatus == STATUS_PENDING) + { + KeWaitForSingleObject (&event, Executive, KernelMode, FALSE, NULL); + ntStatus = ioStatusBlock.Status; + } + +ret: + ObDereferenceObject (fileObject); + return ntStatus; +} + + +typedef struct +{ + PDEVICE_OBJECT deviceObject; ULONG ioControlCode; void *inputBuffer; int inputBufferSize; void *outputBuffer; int outputBufferSize; + NTSTATUS Status; + KEVENT WorkItemCompletedEvent; +} SendDeviceIoControlRequestWorkItemArgs; + + +static VOID SendDeviceIoControlRequestWorkItemRoutine (PDEVICE_OBJECT rootDeviceObject, SendDeviceIoControlRequestWorkItemArgs *arg) +{ + arg->Status = SendDeviceIoControlRequest (arg->deviceObject, arg->ioControlCode, arg->inputBuffer, arg->inputBufferSize, arg->outputBuffer, arg->outputBufferSize); + KeSetEvent (&arg->WorkItemCompletedEvent, IO_NO_INCREMENT, FALSE); +} + + +NTSTATUS SendDeviceIoControlRequest (PDEVICE_OBJECT deviceObject, ULONG ioControlCode, void *inputBuffer, int inputBufferSize, void *outputBuffer, int outputBufferSize) +{ + IO_STATUS_BLOCK ioStatusBlock; + NTSTATUS status; + PIRP irp; + KEVENT event; + + if (KeGetCurrentIrql() > APC_LEVEL) + { + SendDeviceIoControlRequestWorkItemArgs args; + + PIO_WORKITEM workItem = IoAllocateWorkItem (RootDeviceObject); + if (!workItem) + return STATUS_INSUFFICIENT_RESOURCES; + + args.deviceObject = deviceObject; + args.ioControlCode = ioControlCode; + args.inputBuffer = inputBuffer; + args.inputBufferSize = inputBufferSize; + args.outputBuffer = outputBuffer; + args.outputBufferSize = outputBufferSize; + + KeInitializeEvent (&args.WorkItemCompletedEvent, SynchronizationEvent, FALSE); + IoQueueWorkItem (workItem, SendDeviceIoControlRequestWorkItemRoutine, DelayedWorkQueue, &args); + + KeWaitForSingleObject (&args.WorkItemCompletedEvent, Executive, KernelMode, FALSE, NULL); + IoFreeWorkItem (workItem); + + return args.Status; + } + + KeInitializeEvent (&event, NotificationEvent, FALSE); + + irp = IoBuildDeviceIoControlRequest (ioControlCode, deviceObject, inputBuffer, inputBufferSize, + outputBuffer, outputBufferSize, FALSE, &event, &ioStatusBlock); + + if (!irp) + return STATUS_INSUFFICIENT_RESOURCES; + + ObReferenceObject (deviceObject); + + status = IoCallDriver (deviceObject, irp); + if (status == STATUS_PENDING) + { + KeWaitForSingleObject (&event, Executive, KernelMode, FALSE, NULL); + status = ioStatusBlock.Status; + } + + ObDereferenceObject (deviceObject); + return status; +} + + +NTSTATUS ProbeRealDriveSize (PDEVICE_OBJECT driveDeviceObject, LARGE_INTEGER *driveSize) +{ + NTSTATUS status; + LARGE_INTEGER sysLength; + LARGE_INTEGER offset; + byte *sectorBuffer; + ULONGLONG startTime; + + if (!UserCanAccessDriveDevice()) + return STATUS_ACCESS_DENIED; + + sectorBuffer = TCalloc (TC_SECTOR_SIZE_BIOS); + if (!sectorBuffer) + return STATUS_INSUFFICIENT_RESOURCES; + + status = SendDeviceIoControlRequest (driveDeviceObject, IOCTL_DISK_GET_LENGTH_INFO, + NULL, 0, &sysLength, sizeof (sysLength)); + + if (!NT_SUCCESS (status)) + { + Dump ("Failed to get drive size - error %x\n", status); + TCfree (sectorBuffer); + return status; + } + + startTime = KeQueryInterruptTime (); + for (offset.QuadPart = sysLength.QuadPart; ; offset.QuadPart += TC_SECTOR_SIZE_BIOS) + { + status = TCReadDevice (driveDeviceObject, sectorBuffer, offset, TC_SECTOR_SIZE_BIOS); + + if (NT_SUCCESS (status)) + status = TCWriteDevice (driveDeviceObject, sectorBuffer, offset, TC_SECTOR_SIZE_BIOS); + + if (!NT_SUCCESS (status)) + { + driveSize->QuadPart = offset.QuadPart; + Dump ("Real drive size = %I64d bytes (%I64d hidden)\n", driveSize->QuadPart, driveSize->QuadPart - sysLength.QuadPart); + TCfree (sectorBuffer); + return STATUS_SUCCESS; + } + + if (KeQueryInterruptTime() - startTime > 3ULL * 60 * 1000 * 1000 * 10) + { + // Abort if probing for more than 3 minutes + driveSize->QuadPart = sysLength.QuadPart; + TCfree (sectorBuffer); + return STATUS_TIMEOUT; + } + } +} + + +NTSTATUS TCOpenFsVolume (PEXTENSION Extension, PHANDLE volumeHandle, PFILE_OBJECT * fileObject) +{ + NTSTATUS ntStatus; + OBJECT_ATTRIBUTES objectAttributes; + UNICODE_STRING fullFileName; + IO_STATUS_BLOCK ioStatus; + WCHAR volumeName[TC_MAX_PATH]; + + TCGetNTNameFromNumber (volumeName, sizeof(volumeName),Extension->nDosDriveNo); + RtlInitUnicodeString (&fullFileName, volumeName); + InitializeObjectAttributes (&objectAttributes, &fullFileName, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, NULL, NULL); + + ntStatus = ZwCreateFile (volumeHandle, + SYNCHRONIZE | GENERIC_READ, + &objectAttributes, + &ioStatus, + NULL, + FILE_ATTRIBUTE_NORMAL, + FILE_SHARE_READ | FILE_SHARE_WRITE, + FILE_OPEN, + FILE_SYNCHRONOUS_IO_NONALERT, + NULL, + 0); + + Dump ("Volume %ls open NTSTATUS 0x%08x\n", volumeName, ntStatus); + + if (!NT_SUCCESS (ntStatus)) + return ntStatus; + + ntStatus = ObReferenceObjectByHandle (*volumeHandle, + FILE_READ_DATA, + NULL, + KernelMode, + fileObject, + NULL); + + if (!NT_SUCCESS (ntStatus)) + ZwClose (*volumeHandle); + + return ntStatus; +} + + +void TCCloseFsVolume (HANDLE volumeHandle, PFILE_OBJECT fileObject) +{ + ObDereferenceObject (fileObject); + ZwClose (volumeHandle); +} + + +static NTSTATUS TCReadWriteDevice (BOOL write, PDEVICE_OBJECT deviceObject, PVOID buffer, LARGE_INTEGER offset, ULONG length) +{ + NTSTATUS status; + IO_STATUS_BLOCK ioStatusBlock; + PIRP irp; + KEVENT completionEvent; + + ASSERT (KeGetCurrentIrql() <= APC_LEVEL); + + KeInitializeEvent (&completionEvent, NotificationEvent, FALSE); + irp = IoBuildSynchronousFsdRequest (write ? IRP_MJ_WRITE : IRP_MJ_READ, deviceObject, buffer, length, &offset, &completionEvent, &ioStatusBlock); + if (!irp) + return STATUS_INSUFFICIENT_RESOURCES; + + ObReferenceObject (deviceObject); + status = IoCallDriver (deviceObject, irp); + + if (status == STATUS_PENDING) + { + status = KeWaitForSingleObject (&completionEvent, Executive, KernelMode, FALSE, NULL); + if (NT_SUCCESS (status)) + status = ioStatusBlock.Status; + } + + ObDereferenceObject (deviceObject); + return status; +} + + +NTSTATUS TCReadDevice (PDEVICE_OBJECT deviceObject, PVOID buffer, LARGE_INTEGER offset, ULONG length) +{ + return TCReadWriteDevice (FALSE, deviceObject, buffer, offset, length); +} + + +NTSTATUS TCWriteDevice (PDEVICE_OBJECT deviceObject, PVOID buffer, LARGE_INTEGER offset, ULONG length) +{ + return TCReadWriteDevice (TRUE, deviceObject, buffer, offset, length); +} + + +NTSTATUS TCFsctlCall (PFILE_OBJECT fileObject, LONG IoControlCode, + void *InputBuffer, int InputBufferSize, void *OutputBuffer, int OutputBufferSize) +{ + IO_STATUS_BLOCK ioStatusBlock; + NTSTATUS ntStatus; + PIRP irp; + KEVENT event; + PIO_STACK_LOCATION stack; + PDEVICE_OBJECT deviceObject = IoGetRelatedDeviceObject (fileObject); + + KeInitializeEvent(&event, NotificationEvent, FALSE); + + irp = IoBuildDeviceIoControlRequest (IoControlCode, + deviceObject, + InputBuffer, InputBufferSize, + OutputBuffer, OutputBufferSize, + FALSE, + &event, + &ioStatusBlock); + + if (irp == NULL) + return STATUS_INSUFFICIENT_RESOURCES; + + stack = IoGetNextIrpStackLocation(irp); + + stack->MajorFunction = IRP_MJ_FILE_SYSTEM_CONTROL; + stack->MinorFunction = IRP_MN_USER_FS_REQUEST; + stack->FileObject = fileObject; + + ntStatus = IoCallDriver (deviceObject, irp); + if (ntStatus == STATUS_PENDING) + { + KeWaitForSingleObject (&event, Executive, KernelMode, FALSE, NULL); + ntStatus = ioStatusBlock.Status; + } + + return ntStatus; +} + + +NTSTATUS CreateDriveLink (int nDosDriveNo) +{ + WCHAR dev[128], link[128]; + UNICODE_STRING deviceName, symLink; + NTSTATUS ntStatus; + + TCGetNTNameFromNumber (dev, sizeof(dev),nDosDriveNo); + TCGetDosNameFromNumber (link, sizeof(link),nDosDriveNo, DeviceNamespaceDefault); + + RtlInitUnicodeString (&deviceName, dev); + RtlInitUnicodeString (&symLink, link); + + ntStatus = IoCreateSymbolicLink (&symLink, &deviceName); + Dump ("IoCreateSymbolicLink returned %X\n", ntStatus); + return ntStatus; +} + + +NTSTATUS RemoveDriveLink (int nDosDriveNo) +{ + WCHAR link[256]; + UNICODE_STRING symLink; + NTSTATUS ntStatus; + + TCGetDosNameFromNumber (link, sizeof(link),nDosDriveNo, DeviceNamespaceDefault); + RtlInitUnicodeString (&symLink, link); + + ntStatus = IoDeleteSymbolicLink (&symLink); + Dump ("IoDeleteSymbolicLink returned %X\n", ntStatus); + return ntStatus; +} + + +NTSTATUS MountManagerMount (MOUNT_STRUCT *mount) +{ + NTSTATUS ntStatus; + WCHAR arrVolume[256]; + char buf[200]; + PMOUNTMGR_TARGET_NAME in = (PMOUNTMGR_TARGET_NAME) buf; + PMOUNTMGR_CREATE_POINT_INPUT point = (PMOUNTMGR_CREATE_POINT_INPUT) buf; + + TCGetNTNameFromNumber (arrVolume, sizeof(arrVolume),mount->nDosDriveNo); + in->DeviceNameLength = (USHORT) wcslen (arrVolume) * 2; + RtlStringCbCopyW(in->DeviceName, sizeof(buf) - sizeof(in->DeviceNameLength),arrVolume); + + ntStatus = TCDeviceIoControl (MOUNTMGR_DEVICE_NAME, IOCTL_MOUNTMGR_VOLUME_ARRIVAL_NOTIFICATION, + in, (ULONG) (sizeof (in->DeviceNameLength) + wcslen (arrVolume) * 2), 0, 0); + + memset (buf, 0, sizeof buf); + TCGetDosNameFromNumber ((PWSTR) &point[1], sizeof(buf) - sizeof(MOUNTMGR_CREATE_POINT_INPUT),mount->nDosDriveNo, DeviceNamespaceDefault); + + point->SymbolicLinkNameOffset = sizeof (MOUNTMGR_CREATE_POINT_INPUT); + point->SymbolicLinkNameLength = (USHORT) wcslen ((PWSTR) &point[1]) * 2; + + point->DeviceNameOffset = point->SymbolicLinkNameOffset + point->SymbolicLinkNameLength; + TCGetNTNameFromNumber ((PWSTR) (buf + point->DeviceNameOffset), sizeof(buf) - point->DeviceNameOffset,mount->nDosDriveNo); + point->DeviceNameLength = (USHORT) wcslen ((PWSTR) (buf + point->DeviceNameOffset)) * 2; + + ntStatus = TCDeviceIoControl (MOUNTMGR_DEVICE_NAME, IOCTL_MOUNTMGR_CREATE_POINT, point, + point->DeviceNameOffset + point->DeviceNameLength, 0, 0); + + return ntStatus; +} + + +NTSTATUS MountManagerUnmount (int nDosDriveNo) +{ + NTSTATUS ntStatus; + char buf[256], out[300]; + PMOUNTMGR_MOUNT_POINT in = (PMOUNTMGR_MOUNT_POINT) buf; + + memset (buf, 0, sizeof buf); + + TCGetDosNameFromNumber ((PWSTR) &in[1], sizeof(buf) - sizeof(MOUNTMGR_MOUNT_POINT),nDosDriveNo, DeviceNamespaceDefault); + + // Only symbolic link can be deleted with IOCTL_MOUNTMGR_DELETE_POINTS. If any other entry is specified, the mount manager will ignore subsequent IOCTL_MOUNTMGR_VOLUME_ARRIVAL_NOTIFICATION for the same volume ID. + in->SymbolicLinkNameOffset = sizeof (MOUNTMGR_MOUNT_POINT); + in->SymbolicLinkNameLength = (USHORT) wcslen ((PWCHAR) &in[1]) * 2; + + ntStatus = TCDeviceIoControl (MOUNTMGR_DEVICE_NAME, IOCTL_MOUNTMGR_DELETE_POINTS, + in, sizeof(MOUNTMGR_MOUNT_POINT) + in->SymbolicLinkNameLength, out, sizeof out); + + Dump ("IOCTL_MOUNTMGR_DELETE_POINTS returned 0x%08x\n", ntStatus); + + return ntStatus; +} + + +NTSTATUS MountDevice (PDEVICE_OBJECT DeviceObject, MOUNT_STRUCT *mount) +{ + PDEVICE_OBJECT NewDeviceObject; + NTSTATUS ntStatus; + + // Make sure the user is asking for a reasonable nDosDriveNo + if (mount->nDosDriveNo >= 0 && mount->nDosDriveNo <= 25 + && IsDriveLetterAvailable (mount->nDosDriveNo, DeviceNamespaceDefault) // drive letter must not exist both locally and globally + && IsDriveLetterAvailable (mount->nDosDriveNo, DeviceNamespaceGlobal) + ) + { + Dump ("Mount request looks valid\n"); + } + else + { + Dump ("WARNING: MOUNT DRIVE LETTER INVALID\n"); + mount->nReturnCode = ERR_DRIVE_NOT_FOUND; + return ERR_DRIVE_NOT_FOUND; + } + + if (!SelfTestsPassed) + { + mount->nReturnCode = ERR_SELF_TESTS_FAILED; + return ERR_SELF_TESTS_FAILED; + } + + ntStatus = TCCreateDeviceObject (DeviceObject->DriverObject, &NewDeviceObject, mount); + + if (!NT_SUCCESS (ntStatus)) + { + Dump ("Mount CREATE DEVICE ERROR, ntStatus = 0x%08x\n", ntStatus); + return ntStatus; + } + else + { + PEXTENSION NewExtension = (PEXTENSION) NewDeviceObject->DeviceExtension; + SECURITY_SUBJECT_CONTEXT subContext; + PACCESS_TOKEN accessToken; + + SeCaptureSubjectContext (&subContext); + SeLockSubjectContext(&subContext); + if (subContext.ClientToken && subContext.ImpersonationLevel >= SecurityImpersonation) + accessToken = subContext.ClientToken; + else + accessToken = subContext.PrimaryToken; + + if (!accessToken) + { + ntStatus = STATUS_INVALID_PARAMETER; + } + else + { + PTOKEN_USER tokenUser; + + ntStatus = SeQueryInformationToken (accessToken, TokenUser, &tokenUser); + if (NT_SUCCESS (ntStatus)) + { + ULONG sidLength = RtlLengthSid (tokenUser->User.Sid); + + NewExtension->UserSid = TCalloc (sidLength); + if (!NewExtension->UserSid) + ntStatus = STATUS_INSUFFICIENT_RESOURCES; + else + ntStatus = RtlCopySid (sidLength, NewExtension->UserSid, tokenUser->User.Sid); + + ExFreePool (tokenUser); // Documented in newer versions of WDK + } + } + + SeUnlockSubjectContext(&subContext); + SeReleaseSubjectContext (&subContext); + + if (NT_SUCCESS (ntStatus)) + ntStatus = TCStartVolumeThread (NewDeviceObject, NewExtension, mount); + + if (!NT_SUCCESS (ntStatus)) + { + Dump ("Mount FAILURE NT ERROR, ntStatus = 0x%08x\n", ntStatus); + TCDeleteDeviceObject (NewDeviceObject, NewExtension); + return ntStatus; + } + else + { + if (mount->nReturnCode == 0) + { + HANDLE volumeHandle; + PFILE_OBJECT volumeFileObject; + ULONG labelLen = (ULONG) wcslen (mount->wszLabel); + BOOL bIsNTFS = FALSE; + ULONG labelMaxLen, labelEffectiveLen; + + Dump ("Mount SUCCESS TC code = 0x%08x READ-ONLY = %d\n", mount->nReturnCode, NewExtension->bReadOnly); + + if (NewExtension->bReadOnly) + NewDeviceObject->Characteristics |= FILE_READ_ONLY_DEVICE; + + NewDeviceObject->Flags &= ~DO_DEVICE_INITIALIZING; + + NewExtension->UniqueVolumeId = LastUniqueVolumeId++; + + // check again that the drive letter is available globally and locally + if ( !IsDriveLetterAvailable (mount->nDosDriveNo, DeviceNamespaceDefault) + || !IsDriveLetterAvailable (mount->nDosDriveNo, DeviceNamespaceGlobal) + ) + { + TCDeleteDeviceObject (NewDeviceObject, NewExtension); + mount->nReturnCode = ERR_DRIVE_NOT_FOUND; + return ERR_DRIVE_NOT_FOUND; + } + + if (mount->bMountManager) + MountManagerMount (mount); + + NewExtension->bMountManager = mount->bMountManager; + + // We create symbolic link even if mount manager is notified of + // arriving volume as it apparently sometimes fails to create the link + CreateDriveLink (mount->nDosDriveNo); + + mount->FilesystemDirty = FALSE; + + if (NT_SUCCESS (TCOpenFsVolume (NewExtension, &volumeHandle, &volumeFileObject))) + { + __try + { + ULONG fsStatus; + + if (NT_SUCCESS (TCFsctlCall (volumeFileObject, FSCTL_IS_VOLUME_DIRTY, NULL, 0, &fsStatus, sizeof (fsStatus))) + && (fsStatus & VOLUME_IS_DIRTY)) + { + mount->FilesystemDirty = TRUE; + } + } + __except (EXCEPTION_EXECUTE_HANDLER) + { + mount->FilesystemDirty = TRUE; + } + + // detect if the filesystem is NTFS or FAT + __try + { + NTFS_VOLUME_DATA_BUFFER ntfsData; + if (NT_SUCCESS (TCFsctlCall (volumeFileObject, FSCTL_GET_NTFS_VOLUME_DATA, NULL, 0, &ntfsData, sizeof (ntfsData)))) + { + bIsNTFS = TRUE; + } + } + __except (EXCEPTION_EXECUTE_HANDLER) + { + bIsNTFS = FALSE; + } + + NewExtension->bIsNTFS = bIsNTFS; + mount->bIsNTFS = bIsNTFS; + + if (labelLen > 0) + { + if (bIsNTFS) + labelMaxLen = 32; // NTFS maximum label length + else + labelMaxLen = 11; // FAT maximum label length + + // calculate label effective length + labelEffectiveLen = labelLen > labelMaxLen? labelMaxLen : labelLen; + + // correct the label in the device + memset (&NewExtension->wszLabel[labelEffectiveLen], 0, 33 - labelEffectiveLen); + memcpy (mount->wszLabel, NewExtension->wszLabel, 33); + + // set the volume label + __try + { + IO_STATUS_BLOCK ioblock; + ULONG labelInfoSize = sizeof(FILE_FS_LABEL_INFORMATION) + (labelEffectiveLen * sizeof(WCHAR)); + FILE_FS_LABEL_INFORMATION* labelInfo = (FILE_FS_LABEL_INFORMATION*) TCalloc (labelInfoSize); + labelInfo->VolumeLabelLength = labelEffectiveLen * sizeof(WCHAR); + memcpy (labelInfo->VolumeLabel, mount->wszLabel, labelInfo->VolumeLabelLength); + + if (STATUS_SUCCESS == ZwSetVolumeInformationFile (volumeHandle, &ioblock, labelInfo, labelInfoSize, FileFsLabelInformation)) + { + mount->bDriverSetLabel = TRUE; + NewExtension->bDriverSetLabel = TRUE; + } + + TCfree(labelInfo); + } + __except (EXCEPTION_EXECUTE_HANDLER) + { + + } + } + + TCCloseFsVolume (volumeHandle, volumeFileObject); + } + } + else + { + Dump ("Mount FAILURE TC code = 0x%08x\n", mount->nReturnCode); + TCDeleteDeviceObject (NewDeviceObject, NewExtension); + } + + return STATUS_SUCCESS; + } + } +} + +NTSTATUS UnmountDevice (UNMOUNT_STRUCT *unmountRequest, PDEVICE_OBJECT deviceObject, BOOL ignoreOpenFiles) +{ + PEXTENSION extension = deviceObject->DeviceExtension; + NTSTATUS ntStatus; + HANDLE volumeHandle; + PFILE_OBJECT volumeFileObject; + + Dump ("UnmountDevice %d\n", extension->nDosDriveNo); + + ntStatus = TCOpenFsVolume (extension, &volumeHandle, &volumeFileObject); + + if (NT_SUCCESS (ntStatus)) + { + int dismountRetry; + + // Dismounting a writable NTFS filesystem prevents the driver from being unloaded on Windows 7 + if (IsOSAtLeast (WIN_7) && !extension->bReadOnly) + { + NTFS_VOLUME_DATA_BUFFER ntfsData; + + if (NT_SUCCESS (TCFsctlCall (volumeFileObject, FSCTL_GET_NTFS_VOLUME_DATA, NULL, 0, &ntfsData, sizeof (ntfsData)))) + DriverUnloadDisabled = TRUE; + } + + // Lock volume + ntStatus = TCFsctlCall (volumeFileObject, FSCTL_LOCK_VOLUME, NULL, 0, NULL, 0); + Dump ("FSCTL_LOCK_VOLUME returned %X\n", ntStatus); + + if (!NT_SUCCESS (ntStatus) && !ignoreOpenFiles) + { + TCCloseFsVolume (volumeHandle, volumeFileObject); + return ERR_FILES_OPEN; + } + + // Dismount volume + for (dismountRetry = 0; dismountRetry < 200; ++dismountRetry) + { + ntStatus = TCFsctlCall (volumeFileObject, FSCTL_DISMOUNT_VOLUME, NULL, 0, NULL, 0); + Dump ("FSCTL_DISMOUNT_VOLUME returned %X\n", ntStatus); + + if (NT_SUCCESS (ntStatus) || ntStatus == STATUS_VOLUME_DISMOUNTED) + break; + + if (!ignoreOpenFiles) + { + TCCloseFsVolume (volumeHandle, volumeFileObject); + return ERR_FILES_OPEN; + } + + TCSleep (100); + } + } + else + { + // Volume cannot be opened => force dismount if allowed + if (!ignoreOpenFiles) + return ERR_FILES_OPEN; + else + volumeHandle = NULL; + } + + if (extension->bMountManager) + MountManagerUnmount (extension->nDosDriveNo); + + // We always remove symbolic link as mount manager might fail to do so + RemoveDriveLink (extension->nDosDriveNo); + + extension->bShuttingDown = TRUE; + + ntStatus = IoAcquireRemoveLock (&extension->Queue.RemoveLock, NULL); + ASSERT (NT_SUCCESS (ntStatus)); + IoReleaseRemoveLockAndWait (&extension->Queue.RemoveLock, NULL); + + if (volumeHandle != NULL) + TCCloseFsVolume (volumeHandle, volumeFileObject); + + if (unmountRequest) + { + PCRYPTO_INFO cryptoInfo = ((PEXTENSION) deviceObject->DeviceExtension)->cryptoInfo; + unmountRequest->HiddenVolumeProtectionTriggered = (cryptoInfo->bProtectHiddenVolume && cryptoInfo->bHiddenVolProtectionAction); + } + + TCDeleteDeviceObject (deviceObject, (PEXTENSION) deviceObject->DeviceExtension); + return 0; +} + + +static PDEVICE_OBJECT FindVolumeWithHighestUniqueId (int maxUniqueId) +{ + PDEVICE_OBJECT highestIdDevice = NULL; + int highestId = -1; + int drive; + + for (drive = MIN_MOUNTED_VOLUME_DRIVE_NUMBER; drive <= MAX_MOUNTED_VOLUME_DRIVE_NUMBER; ++drive) + { + PDEVICE_OBJECT device = GetVirtualVolumeDeviceObject (drive); + if (device) + { + PEXTENSION extension = (PEXTENSION) device->DeviceExtension; + if (extension->UniqueVolumeId > highestId && extension->UniqueVolumeId <= maxUniqueId) + { + highestId = extension->UniqueVolumeId; + highestIdDevice = device; + } + } + } + + return highestIdDevice; +} + + +NTSTATUS UnmountAllDevices (UNMOUNT_STRUCT *unmountRequest, BOOL ignoreOpenFiles) +{ + NTSTATUS status = 0; + PDEVICE_OBJECT ListDevice; + int maxUniqueId = LastUniqueVolumeId; + + Dump ("Unmounting all volumes\n"); + + if (unmountRequest) + unmountRequest->HiddenVolumeProtectionTriggered = FALSE; + + // Dismount volumes in the reverse order they were mounted to properly dismount nested volumes + while ((ListDevice = FindVolumeWithHighestUniqueId (maxUniqueId)) != NULL) + { + PEXTENSION ListExtension = (PEXTENSION) ListDevice->DeviceExtension; + maxUniqueId = ListExtension->UniqueVolumeId - 1; + + if (IsVolumeAccessibleByCurrentUser (ListExtension)) + { + NTSTATUS ntStatus; + + if (unmountRequest) + unmountRequest->nDosDriveNo = ListExtension->nDosDriveNo; + + ntStatus = UnmountDevice (unmountRequest, ListDevice, ignoreOpenFiles); + status = ntStatus == 0 ? status : ntStatus; + + if (unmountRequest && unmountRequest->HiddenVolumeProtectionTriggered) + break; + } + } + + return status; +} + +// Resolves symbolic link name to its target name +NTSTATUS SymbolicLinkToTarget (PWSTR symlinkName, PWSTR targetName, USHORT maxTargetNameLength) +{ + NTSTATUS ntStatus; + OBJECT_ATTRIBUTES objectAttributes; + UNICODE_STRING fullFileName; + HANDLE handle; + + RtlInitUnicodeString (&fullFileName, symlinkName); + InitializeObjectAttributes (&objectAttributes, &fullFileName, OBJ_KERNEL_HANDLE | OBJ_CASE_INSENSITIVE, NULL, NULL); + + ntStatus = ZwOpenSymbolicLinkObject (&handle, GENERIC_READ, &objectAttributes); + + if (NT_SUCCESS (ntStatus)) + { + UNICODE_STRING target; + target.Buffer = targetName; + target.Length = 0; + target.MaximumLength = maxTargetNameLength; + memset (targetName, 0, maxTargetNameLength); + + ntStatus = ZwQuerySymbolicLinkObject (handle, &target, NULL); + + ZwClose (handle); + } + + return ntStatus; +} + + +// Checks if two regions overlap (borders are parts of regions) +BOOL RegionsOverlap (unsigned __int64 start1, unsigned __int64 end1, unsigned __int64 start2, unsigned __int64 end2) +{ + return (start1 < start2) ? (end1 >= start2) : (start1 <= end2); +} + + +void GetIntersection (uint64 start1, uint32 length1, uint64 start2, uint64 end2, uint64 *intersectStart, uint32 *intersectLength) +{ + uint64 end1 = start1 + length1 - 1; + uint64 intersectEnd = (end1 <= end2) ? end1 : end2; + + *intersectStart = (start1 >= start2) ? start1 : start2; + *intersectLength = (uint32) ((*intersectStart > intersectEnd) ? 0 : intersectEnd + 1 - *intersectStart); + + if (*intersectLength == 0) + *intersectStart = start1; +} + + +BOOL IsAccessibleByUser (PUNICODE_STRING objectFileName, BOOL readOnly) +{ + OBJECT_ATTRIBUTES fileObjAttributes; + IO_STATUS_BLOCK ioStatusBlock; + HANDLE fileHandle; + NTSTATUS status; + + ASSERT (!IoIsSystemThread (PsGetCurrentThread())); + + InitializeObjectAttributes (&fileObjAttributes, objectFileName, OBJ_CASE_INSENSITIVE | OBJ_FORCE_ACCESS_CHECK | OBJ_KERNEL_HANDLE, NULL, NULL); + + status = ZwCreateFile (&fileHandle, + readOnly ? GENERIC_READ : GENERIC_READ | GENERIC_WRITE, + &fileObjAttributes, + &ioStatusBlock, + NULL, + FILE_ATTRIBUTE_NORMAL, + FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, + FILE_OPEN, + FILE_SYNCHRONOUS_IO_NONALERT, + NULL, + 0); + + if (NT_SUCCESS (status)) + { + ZwClose (fileHandle); + return TRUE; + } + + return FALSE; +} + + +BOOL UserCanAccessDriveDevice () +{ + UNICODE_STRING name; + RtlInitUnicodeString (&name, L"\\Device\\MountPointManager"); + + return IsAccessibleByUser (&name, FALSE); +} + +BOOL IsDriveLetterAvailable (int nDosDriveNo, DeviceNamespaceType namespaceType) +{ + OBJECT_ATTRIBUTES objectAttributes; + UNICODE_STRING objectName; + WCHAR link[128]; + HANDLE handle; + NTSTATUS ntStatus; + + TCGetDosNameFromNumber (link, sizeof(link),nDosDriveNo, namespaceType); + RtlInitUnicodeString (&objectName, link); + InitializeObjectAttributes (&objectAttributes, &objectName, OBJ_KERNEL_HANDLE | OBJ_CASE_INSENSITIVE, NULL, NULL); + + if (NT_SUCCESS (ntStatus = ZwOpenSymbolicLinkObject (&handle, GENERIC_READ, &objectAttributes))) + { + ZwClose (handle); + return FALSE; + } + + return (ntStatus == STATUS_OBJECT_NAME_NOT_FOUND)? TRUE : FALSE; +} + + +NTSTATUS TCCompleteIrp (PIRP irp, NTSTATUS status, ULONG_PTR information) +{ + irp->IoStatus.Status = status; + irp->IoStatus.Information = information; + IoCompleteRequest (irp, IO_NO_INCREMENT); + return status; +} + + +NTSTATUS TCCompleteDiskIrp (PIRP irp, NTSTATUS status, ULONG_PTR information) +{ + irp->IoStatus.Status = status; + irp->IoStatus.Information = information; + IoCompleteRequest (irp, NT_SUCCESS (status) ? IO_DISK_INCREMENT : IO_NO_INCREMENT); + return status; +} + + +size_t GetCpuCount () +{ + KAFFINITY activeCpuMap = KeQueryActiveProcessors(); + size_t mapSize = sizeof (activeCpuMap) * 8; + size_t cpuCount = 0; + + while (mapSize--) + { + if (activeCpuMap & 1) + ++cpuCount; + + activeCpuMap >>= 1; + } + + if (cpuCount == 0) + return 1; + + return cpuCount; +} + + +void EnsureNullTerminatedString (wchar_t *str, size_t maxSizeInBytes) +{ + ASSERT ((maxSizeInBytes & 1) == 0); + str[maxSizeInBytes / sizeof (wchar_t) - 1] = 0; +} + + +void *AllocateMemoryWithTimeout (size_t size, int retryDelay, int timeout) +{ + LARGE_INTEGER waitInterval; + waitInterval.QuadPart = retryDelay * -10000; + + ASSERT (KeGetCurrentIrql() <= APC_LEVEL); + ASSERT (retryDelay > 0 && retryDelay <= timeout); + + while (TRUE) + { + void *memory = TCalloc (size); + if (memory) + return memory; + + timeout -= retryDelay; + if (timeout <= 0) + break; + + KeDelayExecutionThread (KernelMode, FALSE, &waitInterval); + } + + return NULL; +} + + +NTSTATUS TCReadRegistryKey (PUNICODE_STRING keyPath, wchar_t *keyValueName, PKEY_VALUE_PARTIAL_INFORMATION *keyData) +{ + OBJECT_ATTRIBUTES regObjAttribs; + HANDLE regKeyHandle; + NTSTATUS status; + UNICODE_STRING valName; + ULONG size = 0; + ULONG resultSize; + + InitializeObjectAttributes (®ObjAttribs, keyPath, OBJ_KERNEL_HANDLE | OBJ_CASE_INSENSITIVE, NULL, NULL); + status = ZwOpenKey (®KeyHandle, KEY_READ, ®ObjAttribs); + if (!NT_SUCCESS (status)) + return status; + + RtlInitUnicodeString (&valName, keyValueName); + status = ZwQueryValueKey (regKeyHandle, &valName, KeyValuePartialInformation, NULL, 0, &size); + + if (!NT_SUCCESS (status) && status != STATUS_BUFFER_OVERFLOW && status != STATUS_BUFFER_TOO_SMALL) + { + ZwClose (regKeyHandle); + return status; + } + + if (size == 0) + { + ZwClose (regKeyHandle); + return STATUS_NO_DATA_DETECTED; + } + + *keyData = (PKEY_VALUE_PARTIAL_INFORMATION) TCalloc (size); + if (!*keyData) + { + ZwClose (regKeyHandle); + return STATUS_INSUFFICIENT_RESOURCES; + } + + status = ZwQueryValueKey (regKeyHandle, &valName, KeyValuePartialInformation, *keyData, size, &resultSize); + + ZwClose (regKeyHandle); + return status; +} + + +NTSTATUS TCWriteRegistryKey (PUNICODE_STRING keyPath, wchar_t *keyValueName, ULONG keyValueType, void *valueData, ULONG valueSize) +{ + OBJECT_ATTRIBUTES regObjAttribs; + HANDLE regKeyHandle; + NTSTATUS status; + UNICODE_STRING valName; + + InitializeObjectAttributes (®ObjAttribs, keyPath, OBJ_KERNEL_HANDLE | OBJ_CASE_INSENSITIVE, NULL, NULL); + status = ZwOpenKey (®KeyHandle, KEY_READ | KEY_WRITE, ®ObjAttribs); + if (!NT_SUCCESS (status)) + return status; + + RtlInitUnicodeString (&valName, keyValueName); + + status = ZwSetValueKey (regKeyHandle, &valName, 0, keyValueType, valueData, valueSize); + + ZwClose (regKeyHandle); + return status; +} + + +BOOL IsVolumeClassFilterRegistered () +{ + UNICODE_STRING name; + NTSTATUS status; + BOOL registered = FALSE; + + PKEY_VALUE_PARTIAL_INFORMATION data; + + RtlInitUnicodeString (&name, L"\\REGISTRY\\MACHINE\\SYSTEM\\CurrentControlSet\\Control\\Class\\{71A27CDD-812A-11D0-BEC7-08002BE2092F}"); + status = TCReadRegistryKey (&name, L"UpperFilters", &data); + + if (NT_SUCCESS (status)) + { + if (data->Type == REG_MULTI_SZ && data->DataLength >= 9 * sizeof (wchar_t)) + { + // Search for the string "veracrypt" + ULONG i; + for (i = 0; i <= data->DataLength - 9 * sizeof (wchar_t); ++i) + { + if (memcmp (data->Data + i, L"veracrypt", 9 * sizeof (wchar_t)) == 0) + { + Dump ("Volume class filter active\n"); + registered = TRUE; + break; + } + } + } + + TCfree (data); + } + + return registered; +} + + +NTSTATUS ReadRegistryConfigFlags (BOOL driverEntry) +{ + PKEY_VALUE_PARTIAL_INFORMATION data; + UNICODE_STRING name; + NTSTATUS status; + uint32 flags = 0; + + RtlInitUnicodeString (&name, L"\\REGISTRY\\MACHINE\\SYSTEM\\CurrentControlSet\\Services\\veracrypt"); + status = TCReadRegistryKey (&name, TC_DRIVER_CONFIG_REG_VALUE_NAME, &data); + + if (NT_SUCCESS (status)) + { + if (data->Type == REG_DWORD) + { + flags = *(uint32 *) data->Data; + Dump ("Configuration flags = 0x%x\n", flags); + + if (driverEntry) + { + if (flags & (TC_DRIVER_CONFIG_CACHE_BOOT_PASSWORD | TC_DRIVER_CONFIG_CACHE_BOOT_PASSWORD_FOR_SYS_FAVORITES)) + CacheBootPassword = TRUE; + + if (flags & TC_DRIVER_CONFIG_DISABLE_NONADMIN_SYS_FAVORITES_ACCESS) + NonAdminSystemFavoritesAccessDisabled = TRUE; + + if (flags & TC_DRIVER_CONFIG_CACHE_BOOT_PIM) + CacheBootPim = TRUE; + } + + EnableHwEncryption ((flags & TC_DRIVER_CONFIG_DISABLE_HARDWARE_ENCRYPTION) ? FALSE : TRUE); + + EnableExtendedIoctlSupport = (flags & TC_DRIVER_CONFIG_ENABLE_EXTENDED_IOCTL)? TRUE : FALSE; + } + else + status = STATUS_INVALID_PARAMETER; + + TCfree (data); + } + + if (driverEntry && NT_SUCCESS (TCReadRegistryKey (&name, TC_ENCRYPTION_FREE_CPU_COUNT_REG_VALUE_NAME, &data))) + { + if (data->Type == REG_DWORD) + EncryptionThreadPoolFreeCpuCountLimit = *(uint32 *) data->Data; + + TCfree (data); + } + + return status; +} + + +NTSTATUS WriteRegistryConfigFlags (uint32 flags) +{ + UNICODE_STRING name; + RtlInitUnicodeString (&name, L"\\REGISTRY\\MACHINE\\SYSTEM\\CurrentControlSet\\Services\\veracrypt"); + + return TCWriteRegistryKey (&name, TC_DRIVER_CONFIG_REG_VALUE_NAME, REG_DWORD, &flags, sizeof (flags)); +} + + +NTSTATUS GetDeviceSectorSize (PDEVICE_OBJECT deviceObject, ULONG *bytesPerSector) +{ + NTSTATUS status; + DISK_GEOMETRY geometry; + + status = SendDeviceIoControlRequest (deviceObject, IOCTL_DISK_GET_DRIVE_GEOMETRY, NULL, 0, &geometry, sizeof (geometry)); + + if (!NT_SUCCESS (status)) + return status; + + *bytesPerSector = geometry.BytesPerSector; + return STATUS_SUCCESS; +} + + +NTSTATUS ZeroUnreadableSectors (PDEVICE_OBJECT deviceObject, LARGE_INTEGER startOffset, ULONG size, uint64 *zeroedSectorCount) +{ + NTSTATUS status; + ULONG sectorSize; + ULONG sectorCount; + byte *sectorBuffer = NULL; + + *zeroedSectorCount = 0; + + status = GetDeviceSectorSize (deviceObject, §orSize); + if (!NT_SUCCESS (status)) + return status; + + sectorBuffer = TCalloc (sectorSize); + if (!sectorBuffer) + return STATUS_INSUFFICIENT_RESOURCES; + + for (sectorCount = size / sectorSize; sectorCount > 0; --sectorCount, startOffset.QuadPart += sectorSize) + { + status = TCReadDevice (deviceObject, sectorBuffer, startOffset, sectorSize); + if (!NT_SUCCESS (status)) + { + Dump ("Zeroing sector at %I64d\n", startOffset.QuadPart); + memset (sectorBuffer, 0, sectorSize); + + status = TCWriteDevice (deviceObject, sectorBuffer, startOffset, sectorSize); + if (!NT_SUCCESS (status)) + goto err; + + ++(*zeroedSectorCount); + } + } + + status = STATUS_SUCCESS; + +err: + if (sectorBuffer) + TCfree (sectorBuffer); + + return status; +} + + +NTSTATUS ReadDeviceSkipUnreadableSectors (PDEVICE_OBJECT deviceObject, byte *buffer, LARGE_INTEGER startOffset, ULONG size, uint64 *badSectorCount) +{ + NTSTATUS status; + ULONG sectorSize; + ULONG sectorCount; + + *badSectorCount = 0; + + status = GetDeviceSectorSize (deviceObject, §orSize); + if (!NT_SUCCESS (status)) + return status; + + for (sectorCount = size / sectorSize; sectorCount > 0; --sectorCount, startOffset.QuadPart += sectorSize, buffer += sectorSize) + { + status = TCReadDevice (deviceObject, buffer, startOffset, sectorSize); + if (!NT_SUCCESS (status)) + { + Dump ("Skipping bad sector at %I64d\n", startOffset.QuadPart); + memset (buffer, 0, sectorSize); + ++(*badSectorCount); + } + } + + return STATUS_SUCCESS; +} + + +BOOL IsVolumeAccessibleByCurrentUser (PEXTENSION volumeDeviceExtension) +{ + SECURITY_SUBJECT_CONTEXT subContext; + PACCESS_TOKEN accessToken; + PTOKEN_USER tokenUser; + BOOL result = FALSE; + + if (IoIsSystemThread (PsGetCurrentThread()) + || UserCanAccessDriveDevice() + || !volumeDeviceExtension->UserSid + || (volumeDeviceExtension->SystemFavorite && !NonAdminSystemFavoritesAccessDisabled)) + { + return TRUE; + } + + SeCaptureSubjectContext (&subContext); + SeLockSubjectContext(&subContext); + if (subContext.ClientToken && subContext.ImpersonationLevel >= SecurityImpersonation) + accessToken = subContext.ClientToken; + else + accessToken = subContext.PrimaryToken; + + if (!accessToken) + goto ret; + + if (SeTokenIsAdmin (accessToken)) + { + result = TRUE; + goto ret; + } + + if (!NT_SUCCESS (SeQueryInformationToken (accessToken, TokenUser, &tokenUser))) + goto ret; + + result = RtlEqualSid (volumeDeviceExtension->UserSid, tokenUser->User.Sid); + ExFreePool (tokenUser); // Documented in newer versions of WDK + +ret: + SeUnlockSubjectContext(&subContext); + SeReleaseSubjectContext (&subContext); + return result; +} + + +void GetElapsedTimeInit (LARGE_INTEGER *lastPerfCounter) +{ + *lastPerfCounter = KeQueryPerformanceCounter (NULL); +} + + +// Returns elapsed time in microseconds since last call +int64 GetElapsedTime (LARGE_INTEGER *lastPerfCounter) +{ + LARGE_INTEGER freq; + LARGE_INTEGER counter = KeQueryPerformanceCounter (&freq); + + int64 elapsed = (counter.QuadPart - lastPerfCounter->QuadPart) * 1000000LL / freq.QuadPart; + *lastPerfCounter = counter; + + return elapsed; +} + + +BOOL IsOSAtLeast (OSVersionEnum reqMinOS) +{ + /* When updating this function, update IsOSVersionAtLeast() in Dlgcode.c too. */ + + ULONG major = 0, minor = 0; + + ASSERT (OsMajorVersion != 0); + + switch (reqMinOS) + { + case WIN_2000: major = 5; minor = 0; break; + case WIN_XP: major = 5; minor = 1; break; + case WIN_SERVER_2003: major = 5; minor = 2; break; + case WIN_VISTA: major = 6; minor = 0; break; + case WIN_7: major = 6; minor = 1; break; + case WIN_8: major = 6; minor = 2; break; + case WIN_8_1: major = 6; minor = 3; break; + case WIN_10: major = 10; minor = 0; break; + + default: + TC_THROW_FATAL_EXCEPTION; + break; + } + + return ((OsMajorVersion << 16 | OsMinorVersion << 8) + >= (major << 16 | minor << 8)); +} diff --git a/src/Driver/Ntdriver.h b/src/Driver/Ntdriver.h index 59634760..d5d548e8 100644 --- a/src/Driver/Ntdriver.h +++ b/src/Driver/Ntdriver.h @@ -1,189 +1,189 @@ -/* - Legal Notice: Some portions of the source code contained in this file were - derived from the source code of TrueCrypt 7.1a, which is - Copyright (c) 2003-2012 TrueCrypt Developers Association and which is - governed by the TrueCrypt License 3.0, also from the source code of - Encryption for the Masses 2.02a, which is Copyright (c) 1998-2000 Paul Le Roux - and which is governed by the 'License Agreement for Encryption for the Masses' - Modifications and additions to the original source code (contained in this file) - and all other portions of this file are Copyright (c) 2013-2016 IDRIX - and are governed by the Apache License 2.0 the full text of which is - contained in the file License.txt included in VeraCrypt binary and source - code distribution packages. */ - -#ifndef TC_HEADER_NTDRIVER -#define TC_HEADER_NTDRIVER - -#include "Common.h" -#include "EncryptedIoQueue.h" - -/* This structure is used to start new threads */ -typedef struct _THREAD_BLOCK_ -{ - PDEVICE_OBJECT DeviceObject; - NTSTATUS ntCreateStatus; - WCHAR wszMountVolume[TC_MAX_PATH + 8]; - MOUNT_STRUCT *mount; -} THREAD_BLOCK, *PTHREAD_BLOCK; - - -/* This structure is allocated for non-root devices! WARNING: bRootDevice - must be the first member of the structure! */ -typedef struct EXTENSION -{ - BOOL bRootDevice; /* Is this the root device ? which the user-mode apps talk to */ - BOOL IsVolumeDevice; - BOOL IsDriveFilterDevice; - BOOL IsVolumeFilterDevice; - - int UniqueVolumeId; - int nDosDriveNo; /* Drive number this extension is mounted against */ - - BOOL bShuttingDown; /* Is the driver shutting down ? */ - BOOL bThreadShouldQuit; /* Instruct per device worker thread to quit */ - PETHREAD peThread; /* Thread handle */ - KEVENT keCreateEvent; /* Device creation event */ - KSPIN_LOCK ListSpinLock; /* IRP spinlock */ - LIST_ENTRY ListEntry; /* IRP listentry */ - KSEMAPHORE RequestSemaphore; /* IRP list request Semaphore */ - - HANDLE hDeviceFile; /* Device handle for this device */ - PFILE_OBJECT pfoDeviceFile; /* Device fileobject for this device */ - PDEVICE_OBJECT pFsdDevice; /* lower level device handle */ - - CRYPTO_INFO *cryptoInfo; /* Cryptographic and other information for this device */ - - __int64 HostLength; - __int64 DiskLength; /* The length of the disk referred to by this device */ - __int64 NumberOfCylinders; /* Partition info */ - ULONG TracksPerCylinder; /* Partition info */ - ULONG SectorsPerTrack; /* Partition info */ - ULONG BytesPerSector; /* Partition info */ - UCHAR PartitionType; /* Partition info */ - - uint32 HostBytesPerSector; - uint32 HostBytesPerPhysicalSector; - ULONG BytesOffsetForSectorAlignment; - - KEVENT keVolumeEvent; /* Event structure used when setting up a device */ - - EncryptedIoQueue Queue; - - BOOL bReadOnly; /* Is this device read-only ? */ - BOOL bRemovable; /* Is this device removable media ? */ - BOOL PartitionInInactiveSysEncScope; - BOOL bRawDevice; /* Is this a raw-partition or raw-floppy device ? */ - BOOL bMountManager; /* Mount manager knows about volume */ - BOOL SystemFavorite; - - WCHAR wszVolume[TC_MAX_PATH]; /* DONT change this size without also changing MOUNT_LIST_STRUCT! */ - WCHAR wszLabel[33]; - BOOL bIsNTFS; - BOOL bDriverSetLabel; - - unsigned char volumeID[VOLUME_ID_SIZE]; - - LARGE_INTEGER fileCreationTime; - LARGE_INTEGER fileLastAccessTime; - LARGE_INTEGER fileLastWriteTime; - LARGE_INTEGER fileLastChangeTime; - BOOL bTimeStampValid; - - PSID UserSid; - BOOL SecurityClientContextValid; - SECURITY_CLIENT_CONTEXT SecurityClientContext; - -} EXTENSION, *PEXTENSION; - - -typedef enum -{ - ValidateInput, - ValidateOutput, - ValidateInputOutput -} ValidateIOBufferSizeType; - -typedef enum -{ - DeviceNamespaceDefault, - DeviceNamespaceGlobal, -} DeviceNamespaceType; - -extern PDRIVER_OBJECT TCDriverObject; -extern PDEVICE_OBJECT RootDeviceObject; -extern BOOL DriverShuttingDown; -extern ULONG OsMajorVersion; -extern ULONG OsMinorVersion; -extern BOOL VolumeClassFilterRegistered; -extern BOOL CacheBootPassword; -extern BOOL CacheBootPim; - -/* Helper macro returning x seconds in units of 100 nanoseconds */ -#define WAIT_SECONDS(x) ((x)*10000000) - -NTSTATUS DriverEntry (PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegistryPath); -NTSTATUS DriverAddDevice (PDRIVER_OBJECT driverObject, PDEVICE_OBJECT pdo); -void DumpMemory (void *memory, int size); -BOOL IsAccessibleByUser (PUNICODE_STRING objectFileName, BOOL readOnly); -NTSTATUS ProcessMainDeviceControlIrp (PDEVICE_OBJECT DeviceObject, PEXTENSION Extension, PIRP Irp); -NTSTATUS ProcessVolumeDeviceControlIrp (PDEVICE_OBJECT DeviceObject, PEXTENSION Extension, PIRP Irp); -NTSTATUS SendDeviceIoControlRequest (PDEVICE_OBJECT deviceObject, ULONG ioControlCode, void *inputBuffer, int inputBufferSize, void *outputBuffer, int outputBufferSize); -NTSTATUS TCDispatchQueueIRP (PDEVICE_OBJECT DeviceObject, PIRP Irp); -NTSTATUS TCCreateRootDeviceObject (PDRIVER_OBJECT DriverObject); -NTSTATUS TCCreateDeviceObject (PDRIVER_OBJECT DriverObject, PDEVICE_OBJECT * ppDeviceObject, MOUNT_STRUCT * mount); -NTSTATUS TCReadDevice (PDEVICE_OBJECT deviceObject, PVOID buffer, LARGE_INTEGER offset, ULONG length); -NTSTATUS TCWriteDevice (PDEVICE_OBJECT deviceObject, PVOID buffer, LARGE_INTEGER offset, ULONG length); -NTSTATUS TCStartThread (PKSTART_ROUTINE threadProc, PVOID threadArg, PKTHREAD *kThread); -NTSTATUS TCStartThreadInProcess (PKSTART_ROUTINE threadProc, PVOID threadArg, PKTHREAD *kThread, PEPROCESS process); -NTSTATUS TCStartVolumeThread (PDEVICE_OBJECT DeviceObject, PEXTENSION Extension, MOUNT_STRUCT * mount); -void TCStopThread (PKTHREAD kThread, PKEVENT wakeUpEvent); -void TCStopVolumeThread (PDEVICE_OBJECT DeviceObject, PEXTENSION Extension); -VOID VolumeThreadProc (PVOID Context); -void TCSleep (int milliSeconds); -void TCGetNTNameFromNumber (LPWSTR ntname, int cbNtName, int nDriveNo); -void TCGetDosNameFromNumber (LPWSTR dosname, int cbDosName, int nDriveNo, DeviceNamespaceType namespaceType); -LPWSTR TCTranslateCode (ULONG ulCode); -void TCDeleteDeviceObject (PDEVICE_OBJECT DeviceObject, PEXTENSION Extension); -VOID TCUnloadDriver (PDRIVER_OBJECT DriverObject); -void OnShutdownPending (); -NTSTATUS TCDeviceIoControl (PWSTR deviceName, ULONG IoControlCode, void *InputBuffer, ULONG InputBufferSize, void *OutputBuffer, ULONG OutputBufferSize); -NTSTATUS TCOpenFsVolume (PEXTENSION Extension, PHANDLE volumeHandle, PFILE_OBJECT * fileObject); -void TCCloseFsVolume (HANDLE volumeHandle, PFILE_OBJECT fileObject); -NTSTATUS TCFsctlCall (PFILE_OBJECT fileObject, LONG IoControlCode, void *InputBuffer, int InputBufferSize, void *OutputBuffer, int OutputBufferSize); -NTSTATUS CreateDriveLink (int nDosDriveNo); -NTSTATUS RemoveDriveLink (int nDosDriveNo); -NTSTATUS MountManagerMount (MOUNT_STRUCT *mount); -NTSTATUS MountManagerUnmount (int nDosDriveNo); -NTSTATUS MountDevice (PDEVICE_OBJECT deviceObject, MOUNT_STRUCT *mount); -NTSTATUS UnmountDevice (UNMOUNT_STRUCT *unmountRequest, PDEVICE_OBJECT deviceObject, BOOL ignoreOpenFiles); -NTSTATUS UnmountAllDevices (UNMOUNT_STRUCT *unmountRequest, BOOL ignoreOpenFiles); -NTSTATUS SymbolicLinkToTarget (PWSTR symlinkName, PWSTR targetName, USHORT maxTargetNameLength); -BOOL RootDeviceControlMutexAcquireNoWait (); -void RootDeviceControlMutexRelease (); -BOOL RegionsOverlap (unsigned __int64 start1, unsigned __int64 end1, unsigned __int64 start2, unsigned __int64 end2); -void GetIntersection (uint64 start1, uint32 length1, uint64 start2, uint64 end2, uint64 *intersectStart, uint32 *intersectLength); -NTSTATUS TCCompleteIrp (PIRP irp, NTSTATUS status, ULONG_PTR information); -NTSTATUS TCCompleteDiskIrp (PIRP irp, NTSTATUS status, ULONG_PTR information); -NTSTATUS ProbeRealDriveSize (PDEVICE_OBJECT driveDeviceObject, LARGE_INTEGER *driveSize); -BOOL UserCanAccessDriveDevice (); -size_t GetCpuCount (); -void EnsureNullTerminatedString (wchar_t *str, size_t maxSizeInBytes); -void *AllocateMemoryWithTimeout (size_t size, int retryDelay, int timeout); -BOOL IsDriveLetterAvailable (int nDosDriveNo, DeviceNamespaceType namespaceType); -NTSTATUS TCReadRegistryKey (PUNICODE_STRING keyPath, wchar_t *keyValueName, PKEY_VALUE_PARTIAL_INFORMATION *keyData); -NTSTATUS TCWriteRegistryKey (PUNICODE_STRING keyPath, wchar_t *keyValueName, ULONG keyValueType, void *valueData, ULONG valueSize); -BOOL IsVolumeClassFilterRegistered (); -NTSTATUS ReadRegistryConfigFlags (BOOL driverEntry); -NTSTATUS WriteRegistryConfigFlags (uint32 flags); -BOOL ValidateIOBufferSize (PIRP irp, size_t requiredBufferSize, ValidateIOBufferSizeType type); -NTSTATUS GetDeviceSectorSize (PDEVICE_OBJECT deviceObject, ULONG *bytesPerSector); -NTSTATUS ZeroUnreadableSectors (PDEVICE_OBJECT deviceObject, LARGE_INTEGER startOffset, ULONG size, uint64 *zeroedSectorCount); -NTSTATUS ReadDeviceSkipUnreadableSectors (PDEVICE_OBJECT deviceObject, byte *buffer, LARGE_INTEGER startOffset, ULONG size, uint64 *badSectorCount); -BOOL IsVolumeAccessibleByCurrentUser (PEXTENSION volumeDeviceExtension); -void GetElapsedTimeInit (LARGE_INTEGER *lastPerfCounter); -int64 GetElapsedTime (LARGE_INTEGER *lastPerfCounter); -BOOL IsOSAtLeast (OSVersionEnum reqMinOS); - -#define TC_BUG_CHECK(status) KeBugCheckEx (SECURITY_SYSTEM, __LINE__, (ULONG_PTR) status, 0, 'VC') - -#endif // TC_HEADER_NTDRIVER +/* + Legal Notice: Some portions of the source code contained in this file were + derived from the source code of TrueCrypt 7.1a, which is + Copyright (c) 2003-2012 TrueCrypt Developers Association and which is + governed by the TrueCrypt License 3.0, also from the source code of + Encryption for the Masses 2.02a, which is Copyright (c) 1998-2000 Paul Le Roux + and which is governed by the 'License Agreement for Encryption for the Masses' + Modifications and additions to the original source code (contained in this file) + and all other portions of this file are Copyright (c) 2013-2016 IDRIX + and are governed by the Apache License 2.0 the full text of which is + contained in the file License.txt included in VeraCrypt binary and source + code distribution packages. */ + +#ifndef TC_HEADER_NTDRIVER +#define TC_HEADER_NTDRIVER + +#include "Common.h" +#include "EncryptedIoQueue.h" + +/* This structure is used to start new threads */ +typedef struct _THREAD_BLOCK_ +{ + PDEVICE_OBJECT DeviceObject; + NTSTATUS ntCreateStatus; + WCHAR wszMountVolume[TC_MAX_PATH + 8]; + MOUNT_STRUCT *mount; +} THREAD_BLOCK, *PTHREAD_BLOCK; + + +/* This structure is allocated for non-root devices! WARNING: bRootDevice + must be the first member of the structure! */ +typedef struct EXTENSION +{ + BOOL bRootDevice; /* Is this the root device ? which the user-mode apps talk to */ + BOOL IsVolumeDevice; + BOOL IsDriveFilterDevice; + BOOL IsVolumeFilterDevice; + + int UniqueVolumeId; + int nDosDriveNo; /* Drive number this extension is mounted against */ + + BOOL bShuttingDown; /* Is the driver shutting down ? */ + BOOL bThreadShouldQuit; /* Instruct per device worker thread to quit */ + PETHREAD peThread; /* Thread handle */ + KEVENT keCreateEvent; /* Device creation event */ + KSPIN_LOCK ListSpinLock; /* IRP spinlock */ + LIST_ENTRY ListEntry; /* IRP listentry */ + KSEMAPHORE RequestSemaphore; /* IRP list request Semaphore */ + + HANDLE hDeviceFile; /* Device handle for this device */ + PFILE_OBJECT pfoDeviceFile; /* Device fileobject for this device */ + PDEVICE_OBJECT pFsdDevice; /* lower level device handle */ + + CRYPTO_INFO *cryptoInfo; /* Cryptographic and other information for this device */ + + __int64 HostLength; + __int64 DiskLength; /* The length of the disk referred to by this device */ + __int64 NumberOfCylinders; /* Partition info */ + ULONG TracksPerCylinder; /* Partition info */ + ULONG SectorsPerTrack; /* Partition info */ + ULONG BytesPerSector; /* Partition info */ + UCHAR PartitionType; /* Partition info */ + + uint32 HostBytesPerSector; + uint32 HostBytesPerPhysicalSector; + ULONG BytesOffsetForSectorAlignment; + + KEVENT keVolumeEvent; /* Event structure used when setting up a device */ + + EncryptedIoQueue Queue; + + BOOL bReadOnly; /* Is this device read-only ? */ + BOOL bRemovable; /* Is this device removable media ? */ + BOOL PartitionInInactiveSysEncScope; + BOOL bRawDevice; /* Is this a raw-partition or raw-floppy device ? */ + BOOL bMountManager; /* Mount manager knows about volume */ + BOOL SystemFavorite; + + WCHAR wszVolume[TC_MAX_PATH]; /* DONT change this size without also changing MOUNT_LIST_STRUCT! */ + WCHAR wszLabel[33]; + BOOL bIsNTFS; + BOOL bDriverSetLabel; + + unsigned char volumeID[VOLUME_ID_SIZE]; + + LARGE_INTEGER fileCreationTime; + LARGE_INTEGER fileLastAccessTime; + LARGE_INTEGER fileLastWriteTime; + LARGE_INTEGER fileLastChangeTime; + BOOL bTimeStampValid; + + PSID UserSid; + BOOL SecurityClientContextValid; + SECURITY_CLIENT_CONTEXT SecurityClientContext; + +} EXTENSION, *PEXTENSION; + + +typedef enum +{ + ValidateInput, + ValidateOutput, + ValidateInputOutput +} ValidateIOBufferSizeType; + +typedef enum +{ + DeviceNamespaceDefault, + DeviceNamespaceGlobal, +} DeviceNamespaceType; + +extern PDRIVER_OBJECT TCDriverObject; +extern PDEVICE_OBJECT RootDeviceObject; +extern BOOL DriverShuttingDown; +extern ULONG OsMajorVersion; +extern ULONG OsMinorVersion; +extern BOOL VolumeClassFilterRegistered; +extern BOOL CacheBootPassword; +extern BOOL CacheBootPim; + +/* Helper macro returning x seconds in units of 100 nanoseconds */ +#define WAIT_SECONDS(x) ((x)*10000000) + +NTSTATUS DriverEntry (PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegistryPath); +NTSTATUS DriverAddDevice (PDRIVER_OBJECT driverObject, PDEVICE_OBJECT pdo); +void DumpMemory (void *memory, int size); +BOOL IsAccessibleByUser (PUNICODE_STRING objectFileName, BOOL readOnly); +NTSTATUS ProcessMainDeviceControlIrp (PDEVICE_OBJECT DeviceObject, PEXTENSION Extension, PIRP Irp); +NTSTATUS ProcessVolumeDeviceControlIrp (PDEVICE_OBJECT DeviceObject, PEXTENSION Extension, PIRP Irp); +NTSTATUS SendDeviceIoControlRequest (PDEVICE_OBJECT deviceObject, ULONG ioControlCode, void *inputBuffer, int inputBufferSize, void *outputBuffer, int outputBufferSize); +NTSTATUS TCDispatchQueueIRP (PDEVICE_OBJECT DeviceObject, PIRP Irp); +NTSTATUS TCCreateRootDeviceObject (PDRIVER_OBJECT DriverObject); +NTSTATUS TCCreateDeviceObject (PDRIVER_OBJECT DriverObject, PDEVICE_OBJECT * ppDeviceObject, MOUNT_STRUCT * mount); +NTSTATUS TCReadDevice (PDEVICE_OBJECT deviceObject, PVOID buffer, LARGE_INTEGER offset, ULONG length); +NTSTATUS TCWriteDevice (PDEVICE_OBJECT deviceObject, PVOID buffer, LARGE_INTEGER offset, ULONG length); +NTSTATUS TCStartThread (PKSTART_ROUTINE threadProc, PVOID threadArg, PKTHREAD *kThread); +NTSTATUS TCStartThreadInProcess (PKSTART_ROUTINE threadProc, PVOID threadArg, PKTHREAD *kThread, PEPROCESS process); +NTSTATUS TCStartVolumeThread (PDEVICE_OBJECT DeviceObject, PEXTENSION Extension, MOUNT_STRUCT * mount); +void TCStopThread (PKTHREAD kThread, PKEVENT wakeUpEvent); +void TCStopVolumeThread (PDEVICE_OBJECT DeviceObject, PEXTENSION Extension); +VOID VolumeThreadProc (PVOID Context); +void TCSleep (int milliSeconds); +void TCGetNTNameFromNumber (LPWSTR ntname, int cbNtName, int nDriveNo); +void TCGetDosNameFromNumber (LPWSTR dosname, int cbDosName, int nDriveNo, DeviceNamespaceType namespaceType); +LPWSTR TCTranslateCode (ULONG ulCode); +void TCDeleteDeviceObject (PDEVICE_OBJECT DeviceObject, PEXTENSION Extension); +VOID TCUnloadDriver (PDRIVER_OBJECT DriverObject); +void OnShutdownPending (); +NTSTATUS TCDeviceIoControl (PWSTR deviceName, ULONG IoControlCode, void *InputBuffer, ULONG InputBufferSize, void *OutputBuffer, ULONG OutputBufferSize); +NTSTATUS TCOpenFsVolume (PEXTENSION Extension, PHANDLE volumeHandle, PFILE_OBJECT * fileObject); +void TCCloseFsVolume (HANDLE volumeHandle, PFILE_OBJECT fileObject); +NTSTATUS TCFsctlCall (PFILE_OBJECT fileObject, LONG IoControlCode, void *InputBuffer, int InputBufferSize, void *OutputBuffer, int OutputBufferSize); +NTSTATUS CreateDriveLink (int nDosDriveNo); +NTSTATUS RemoveDriveLink (int nDosDriveNo); +NTSTATUS MountManagerMount (MOUNT_STRUCT *mount); +NTSTATUS MountManagerUnmount (int nDosDriveNo); +NTSTATUS MountDevice (PDEVICE_OBJECT deviceObject, MOUNT_STRUCT *mount); +NTSTATUS UnmountDevice (UNMOUNT_STRUCT *unmountRequest, PDEVICE_OBJECT deviceObject, BOOL ignoreOpenFiles); +NTSTATUS UnmountAllDevices (UNMOUNT_STRUCT *unmountRequest, BOOL ignoreOpenFiles); +NTSTATUS SymbolicLinkToTarget (PWSTR symlinkName, PWSTR targetName, USHORT maxTargetNameLength); +BOOL RootDeviceControlMutexAcquireNoWait (); +void RootDeviceControlMutexRelease (); +BOOL RegionsOverlap (unsigned __int64 start1, unsigned __int64 end1, unsigned __int64 start2, unsigned __int64 end2); +void GetIntersection (uint64 start1, uint32 length1, uint64 start2, uint64 end2, uint64 *intersectStart, uint32 *intersectLength); +NTSTATUS TCCompleteIrp (PIRP irp, NTSTATUS status, ULONG_PTR information); +NTSTATUS TCCompleteDiskIrp (PIRP irp, NTSTATUS status, ULONG_PTR information); +NTSTATUS ProbeRealDriveSize (PDEVICE_OBJECT driveDeviceObject, LARGE_INTEGER *driveSize); +BOOL UserCanAccessDriveDevice (); +size_t GetCpuCount (); +void EnsureNullTerminatedString (wchar_t *str, size_t maxSizeInBytes); +void *AllocateMemoryWithTimeout (size_t size, int retryDelay, int timeout); +BOOL IsDriveLetterAvailable (int nDosDriveNo, DeviceNamespaceType namespaceType); +NTSTATUS TCReadRegistryKey (PUNICODE_STRING keyPath, wchar_t *keyValueName, PKEY_VALUE_PARTIAL_INFORMATION *keyData); +NTSTATUS TCWriteRegistryKey (PUNICODE_STRING keyPath, wchar_t *keyValueName, ULONG keyValueType, void *valueData, ULONG valueSize); +BOOL IsVolumeClassFilterRegistered (); +NTSTATUS ReadRegistryConfigFlags (BOOL driverEntry); +NTSTATUS WriteRegistryConfigFlags (uint32 flags); +BOOL ValidateIOBufferSize (PIRP irp, size_t requiredBufferSize, ValidateIOBufferSizeType type); +NTSTATUS GetDeviceSectorSize (PDEVICE_OBJECT deviceObject, ULONG *bytesPerSector); +NTSTATUS ZeroUnreadableSectors (PDEVICE_OBJECT deviceObject, LARGE_INTEGER startOffset, ULONG size, uint64 *zeroedSectorCount); +NTSTATUS ReadDeviceSkipUnreadableSectors (PDEVICE_OBJECT deviceObject, byte *buffer, LARGE_INTEGER startOffset, ULONG size, uint64 *badSectorCount); +BOOL IsVolumeAccessibleByCurrentUser (PEXTENSION volumeDeviceExtension); +void GetElapsedTimeInit (LARGE_INTEGER *lastPerfCounter); +int64 GetElapsedTime (LARGE_INTEGER *lastPerfCounter); +BOOL IsOSAtLeast (OSVersionEnum reqMinOS); + +#define TC_BUG_CHECK(status) KeBugCheckEx (SECURITY_SYSTEM, __LINE__, (ULONG_PTR) status, 0, 'VC') + +#endif // TC_HEADER_NTDRIVER diff --git a/src/Driver/Ntvol.c b/src/Driver/Ntvol.c index 4f35323b..34ee2dbb 100644 --- a/src/Driver/Ntvol.c +++ b/src/Driver/Ntvol.c @@ -1,900 +1,900 @@ -/* - Legal Notice: Some portions of the source code contained in this file were - derived from the source code of TrueCrypt 7.1a, which is - Copyright (c) 2003-2012 TrueCrypt Developers Association and which is - governed by the TrueCrypt License 3.0, also from the source code of - Encryption for the Masses 2.02a, which is Copyright (c) 1998-2000 Paul Le Roux - and which is governed by the 'License Agreement for Encryption for the Masses' - Modifications and additions to the original source code (contained in this file) - and all other portions of this file are Copyright (c) 2013-2016 IDRIX - and are governed by the Apache License 2.0 the full text of which is - contained in the file License.txt included in VeraCrypt binary and source - code distribution packages. */ - -#include "TCdefs.h" -#include -#include "Crypto.h" -#include "Volumes.h" - -#include "Apidrvr.h" -#include "DriveFilter.h" -#include "Ntdriver.h" -#include "Ntvol.h" -#include "VolumeFilter.h" - -#include "Boot/Windows/BootCommon.h" - -#include "Cache.h" - -#if 0 && _DEBUG -#define EXTRA_INFO 1 -#endif - -#pragma warning( disable : 4127 ) - -#include - -volatile BOOL ProbingHostDeviceForWrite = FALSE; - - -NTSTATUS TCOpenVolume (PDEVICE_OBJECT DeviceObject, - PEXTENSION Extension, - MOUNT_STRUCT *mount, - PWSTR pwszMountVolume, - BOOL bRawDevice) -{ - FILE_STANDARD_INFORMATION FileStandardInfo; - FILE_BASIC_INFORMATION FileBasicInfo; - OBJECT_ATTRIBUTES oaFileAttributes; - UNICODE_STRING FullFileName; - IO_STATUS_BLOCK IoStatusBlock; - PCRYPTO_INFO cryptoInfoPtr = NULL; - PCRYPTO_INFO tmpCryptoInfo = NULL; - LARGE_INTEGER lDiskLength; - __int64 partitionStartingOffset = 0; - int volumeType; - char *readBuffer = 0; - NTSTATUS ntStatus = 0; - BOOL forceAccessCheck = (!bRawDevice && !(OsMajorVersion == 5 &&OsMinorVersion == 0)); // Windows 2000 does not support OBJ_FORCE_ACCESS_CHECK attribute - BOOL disableBuffering = TRUE; - BOOL exclusiveAccess = mount->bExclusiveAccess; - - Extension->pfoDeviceFile = NULL; - Extension->hDeviceFile = NULL; - Extension->bTimeStampValid = FALSE; - - RtlInitUnicodeString (&FullFileName, pwszMountVolume); - InitializeObjectAttributes (&oaFileAttributes, &FullFileName, OBJ_CASE_INSENSITIVE | (forceAccessCheck ? OBJ_FORCE_ACCESS_CHECK : 0) | OBJ_KERNEL_HANDLE, NULL, NULL); - KeInitializeEvent (&Extension->keVolumeEvent, NotificationEvent, FALSE); - - if (Extension->SecurityClientContextValid) - { - ntStatus = SeImpersonateClientEx (&Extension->SecurityClientContext, NULL); - if (!NT_SUCCESS (ntStatus)) - goto error; - } - - mount->VolumeMountedReadOnlyAfterDeviceWriteProtected = FALSE; - - // If we are opening a device, query its size first - if (bRawDevice) - { - PARTITION_INFORMATION pi; - PARTITION_INFORMATION_EX pix; - LARGE_INTEGER diskLengthInfo; - DISK_GEOMETRY dg; - STORAGE_PROPERTY_QUERY storagePropertyQuery = {0}; - STORAGE_ACCESS_ALIGNMENT_DESCRIPTOR storageDescriptor = {0}; - - ntStatus = IoGetDeviceObjectPointer (&FullFileName, - FILE_READ_DATA | FILE_READ_ATTRIBUTES, - &Extension->pfoDeviceFile, - &Extension->pFsdDevice); - - if (!NT_SUCCESS (ntStatus)) - goto error; - - ntStatus = TCSendHostDeviceIoControlRequest (DeviceObject, Extension, IOCTL_DISK_GET_DRIVE_GEOMETRY, (char *) &dg, sizeof (dg)); - if (!NT_SUCCESS (ntStatus)) - goto error; - - lDiskLength.QuadPart = dg.Cylinders.QuadPart * dg.SectorsPerTrack * dg.TracksPerCylinder * dg.BytesPerSector; - Extension->HostBytesPerSector = dg.BytesPerSector; - - storagePropertyQuery.PropertyId = StorageAccessAlignmentProperty; - storagePropertyQuery.QueryType = PropertyStandardQuery; - - /* IOCTL_STORAGE_QUERY_PROPERTY supported only on Vista and above */ - if (NT_SUCCESS (TCSendHostDeviceIoControlRequestEx (DeviceObject, Extension, IOCTL_STORAGE_QUERY_PROPERTY, - (char*) &storagePropertyQuery, sizeof(storagePropertyQuery), - (char *) &storageDescriptor, sizeof (storageDescriptor)))) - { - Extension->HostBytesPerPhysicalSector = storageDescriptor.BytesPerPhysicalSector; - } - else - { - Extension->HostBytesPerPhysicalSector = dg.BytesPerSector; - } - - // Drive geometry is used only when IOCTL_DISK_GET_PARTITION_INFO fails - if (NT_SUCCESS (TCSendHostDeviceIoControlRequest (DeviceObject, Extension, IOCTL_DISK_GET_PARTITION_INFO_EX, (char *) &pix, sizeof (pix)))) - { - lDiskLength.QuadPart = pix.PartitionLength.QuadPart; - partitionStartingOffset = pix.StartingOffset.QuadPart; - } - // Windows 2000 does not support IOCTL_DISK_GET_PARTITION_INFO_EX - else if (NT_SUCCESS (TCSendHostDeviceIoControlRequest (DeviceObject, Extension, IOCTL_DISK_GET_PARTITION_INFO, (char *) &pi, sizeof (pi)))) - { - lDiskLength.QuadPart = pi.PartitionLength.QuadPart; - partitionStartingOffset = pi.StartingOffset.QuadPart; - } - else if (NT_SUCCESS (TCSendHostDeviceIoControlRequest (DeviceObject, Extension, IOCTL_DISK_GET_LENGTH_INFO, &diskLengthInfo, sizeof (diskLengthInfo)))) - { - lDiskLength = diskLengthInfo; - } - - ProbingHostDeviceForWrite = TRUE; - - if (!mount->bMountReadOnly - && TCSendHostDeviceIoControlRequest (DeviceObject, Extension, - IsHiddenSystemRunning() ? TC_IOCTL_DISK_IS_WRITABLE : IOCTL_DISK_IS_WRITABLE, NULL, 0) == STATUS_MEDIA_WRITE_PROTECTED) - { - mount->bMountReadOnly = TRUE; - DeviceObject->Characteristics |= FILE_READ_ONLY_DEVICE; - mount->VolumeMountedReadOnlyAfterDeviceWriteProtected = TRUE; - } - - ProbingHostDeviceForWrite = FALSE; - - // Some Windows tools (e.g. diskmgmt, diskpart, vssadmin) fail or experience timeouts when there is a raw device - // open for exclusive access. Therefore, exclusive access is used only for file-hosted volumes. - // Applications requiring a consistent device image need to acquire exclusive write access first. This is prevented - // when a device-hosted volume is mounted. - - exclusiveAccess = FALSE; - } - else - { - // Limit the maximum required buffer size - if (mount->BytesPerSector > 128 * BYTES_PER_KB) - { - ntStatus = STATUS_INVALID_PARAMETER; - goto error; - } - - Extension->HostBytesPerSector = mount->BytesPerSector; - Extension->HostBytesPerPhysicalSector = mount->BytesPerPhysicalSector; - - if (Extension->HostBytesPerSector != TC_SECTOR_SIZE_FILE_HOSTED_VOLUME) - disableBuffering = FALSE; - } - - // Open the volume hosting file/device - if (!mount->bMountReadOnly) - { - ntStatus = ZwCreateFile (&Extension->hDeviceFile, - GENERIC_READ | GENERIC_WRITE | SYNCHRONIZE, - &oaFileAttributes, - &IoStatusBlock, - NULL, - FILE_ATTRIBUTE_NORMAL | - FILE_ATTRIBUTE_SYSTEM, - exclusiveAccess ? 0 : FILE_SHARE_READ | FILE_SHARE_WRITE, - FILE_OPEN, - FILE_RANDOM_ACCESS | - FILE_WRITE_THROUGH | - (disableBuffering ? FILE_NO_INTERMEDIATE_BUFFERING : 0) | - FILE_SYNCHRONOUS_IO_NONALERT, - NULL, - 0); - } - - /* 26-4-99 NT for some partitions returns this code, it is really a access denied */ - if (ntStatus == 0xc000001b) - ntStatus = STATUS_ACCESS_DENIED; - - mount->VolumeMountedReadOnlyAfterAccessDenied = FALSE; - - if (mount->bMountReadOnly || ntStatus == STATUS_ACCESS_DENIED) - { - ntStatus = ZwCreateFile (&Extension->hDeviceFile, - GENERIC_READ | SYNCHRONIZE, - &oaFileAttributes, - &IoStatusBlock, - NULL, - FILE_ATTRIBUTE_NORMAL | - FILE_ATTRIBUTE_SYSTEM, - exclusiveAccess ? FILE_SHARE_READ : FILE_SHARE_READ | FILE_SHARE_WRITE, - FILE_OPEN, - FILE_RANDOM_ACCESS | - FILE_WRITE_THROUGH | - (disableBuffering ? FILE_NO_INTERMEDIATE_BUFFERING : 0) | - FILE_SYNCHRONOUS_IO_NONALERT, - NULL, - 0); - - if (NT_SUCCESS (ntStatus) && !mount->bMountReadOnly) - mount->VolumeMountedReadOnlyAfterAccessDenied = TRUE; - - Extension->bReadOnly = TRUE; - DeviceObject->Characteristics |= FILE_READ_ONLY_DEVICE; - } - else - Extension->bReadOnly = FALSE; - - /* 26-4-99 NT for some partitions returns this code, it is really a - access denied */ - if (ntStatus == 0xc000001b) - { - /* Partitions which return this code can still be opened with - FILE_SHARE_READ but this causes NT problems elsewhere in - particular if you do FILE_SHARE_READ NT will die later if - anyone even tries to open the partition (or file for that - matter...) */ - ntStatus = STATUS_SHARING_VIOLATION; - } - - if (!NT_SUCCESS (ntStatus)) - { - goto error; - } - - // If we have opened a file, query its size now - if (bRawDevice == FALSE) - { - ntStatus = ZwQueryInformationFile (Extension->hDeviceFile, - &IoStatusBlock, - &FileBasicInfo, - sizeof (FileBasicInfo), - FileBasicInformation); - - if (NT_SUCCESS (ntStatus)) - { - if (mount->bPreserveTimestamp) - { - Extension->fileCreationTime = FileBasicInfo.CreationTime; - Extension->fileLastAccessTime = FileBasicInfo.LastAccessTime; - Extension->fileLastWriteTime = FileBasicInfo.LastWriteTime; - Extension->fileLastChangeTime = FileBasicInfo.ChangeTime; - Extension->bTimeStampValid = TRUE; - } - - ntStatus = ZwQueryInformationFile (Extension->hDeviceFile, - &IoStatusBlock, - &FileStandardInfo, - sizeof (FileStandardInfo), - FileStandardInformation); - } - - if (!NT_SUCCESS (ntStatus)) - { - Dump ("ZwQueryInformationFile failed while opening file: NTSTATUS 0x%08x\n", - ntStatus); - goto error; - } - - lDiskLength.QuadPart = FileStandardInfo.EndOfFile.QuadPart; - - if (FileBasicInfo.FileAttributes & FILE_ATTRIBUTE_COMPRESSED) - { - Dump ("File \"%ls\" is marked as compressed - not supported!\n", pwszMountVolume); - mount->nReturnCode = ERR_COMPRESSION_NOT_SUPPORTED; - ntStatus = STATUS_SUCCESS; - goto error; - } - - ntStatus = ObReferenceObjectByHandle (Extension->hDeviceFile, - FILE_ALL_ACCESS, - *IoFileObjectType, - KernelMode, - &Extension->pfoDeviceFile, - 0); - - if (!NT_SUCCESS (ntStatus)) - { - goto error; - } - - /* Get the FSD device for the file (probably either NTFS or FAT) */ - Extension->pFsdDevice = IoGetRelatedDeviceObject (Extension->pfoDeviceFile); - } - else - { - // Try to gain "raw" access to the partition in case there is a live filesystem on it (otherwise, - // the NTFS driver guards hidden sectors and prevents mounting using a backup header e.g. after the user - // accidentally quick-formats a dismounted partition-hosted TrueCrypt volume as NTFS). - - PFILE_OBJECT pfoTmpDeviceFile = NULL; - - if (NT_SUCCESS (ObReferenceObjectByHandle (Extension->hDeviceFile, FILE_ALL_ACCESS, *IoFileObjectType, KernelMode, &pfoTmpDeviceFile, NULL)) - && pfoTmpDeviceFile != NULL) - { - TCFsctlCall (pfoTmpDeviceFile, FSCTL_ALLOW_EXTENDED_DASD_IO, NULL, 0, NULL, 0); - ObDereferenceObject (pfoTmpDeviceFile); - } - } - - // Check volume size - if (lDiskLength.QuadPart < TC_MIN_VOLUME_SIZE_LEGACY || lDiskLength.QuadPart > TC_MAX_VOLUME_SIZE) - { - mount->nReturnCode = ERR_VOL_SIZE_WRONG; - ntStatus = STATUS_SUCCESS; - goto error; - } - - Extension->DiskLength = lDiskLength.QuadPart; - Extension->HostLength = lDiskLength.QuadPart; - - readBuffer = TCalloc (max (max (TC_VOLUME_HEADER_EFFECTIVE_SIZE, PAGE_SIZE), Extension->HostBytesPerSector)); - if (readBuffer == NULL) - { - ntStatus = STATUS_INSUFFICIENT_RESOURCES; - goto error; - } - - // Go through all volume types (e.g., normal, hidden) - for (volumeType = TC_VOLUME_TYPE_NORMAL; - volumeType < TC_VOLUME_TYPE_COUNT; - volumeType++) - { - Dump ("Trying to open volume type %d\n", volumeType); - - /* Read the volume header */ - - if (!mount->bPartitionInInactiveSysEncScope - || (mount->bPartitionInInactiveSysEncScope && volumeType == TC_VOLUME_TYPE_HIDDEN)) - { - // Header of a volume that is not within the scope of system encryption, or - // header of a system hidden volume (containing a hidden OS) - - LARGE_INTEGER headerOffset; - - if (mount->UseBackupHeader && lDiskLength.QuadPart <= TC_TOTAL_VOLUME_HEADERS_SIZE) - continue; - - switch (volumeType) - { - case TC_VOLUME_TYPE_NORMAL: - headerOffset.QuadPart = mount->UseBackupHeader ? lDiskLength.QuadPart - TC_VOLUME_HEADER_GROUP_SIZE : TC_VOLUME_HEADER_OFFSET; - break; - - case TC_VOLUME_TYPE_HIDDEN: - if (lDiskLength.QuadPart <= TC_VOLUME_HEADER_GROUP_SIZE) - continue; - - headerOffset.QuadPart = mount->UseBackupHeader ? lDiskLength.QuadPart - TC_HIDDEN_VOLUME_HEADER_OFFSET : TC_HIDDEN_VOLUME_HEADER_OFFSET; - break; - } - - Dump ("Reading volume header at %I64d\n", headerOffset.QuadPart); - - ntStatus = ZwReadFile (Extension->hDeviceFile, - NULL, - NULL, - NULL, - &IoStatusBlock, - readBuffer, - bRawDevice ? max (TC_VOLUME_HEADER_EFFECTIVE_SIZE, Extension->HostBytesPerSector) : TC_VOLUME_HEADER_EFFECTIVE_SIZE, - &headerOffset, - NULL); - } - else - { - // Header of a partition that is within the scope of system encryption - - WCHAR parentDrivePath [47+1] = {0}; - HANDLE hParentDeviceFile = NULL; - UNICODE_STRING FullParentPath; - OBJECT_ATTRIBUTES oaParentFileAttributes; - LARGE_INTEGER parentKeyDataOffset; - - RtlStringCbPrintfW (parentDrivePath, - sizeof (parentDrivePath), - WIDE ("\\Device\\Harddisk%d\\Partition0"), - mount->nPartitionInInactiveSysEncScopeDriveNo); - - Dump ("Mounting partition within scope of system encryption (reading key data from: %ls)\n", parentDrivePath); - - RtlInitUnicodeString (&FullParentPath, parentDrivePath); - InitializeObjectAttributes (&oaParentFileAttributes, &FullParentPath, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, NULL, NULL); - - ntStatus = ZwCreateFile (&hParentDeviceFile, - GENERIC_READ | SYNCHRONIZE, - &oaParentFileAttributes, - &IoStatusBlock, - NULL, - FILE_ATTRIBUTE_NORMAL | - FILE_ATTRIBUTE_SYSTEM, - FILE_SHARE_READ | FILE_SHARE_WRITE, - FILE_OPEN, - FILE_RANDOM_ACCESS | - FILE_WRITE_THROUGH | - FILE_NO_INTERMEDIATE_BUFFERING | - FILE_SYNCHRONOUS_IO_NONALERT, - NULL, - 0); - - if (!NT_SUCCESS (ntStatus)) - { - if (hParentDeviceFile != NULL) - ZwClose (hParentDeviceFile); - - Dump ("Cannot open %ls\n", parentDrivePath); - - goto error; - } - - parentKeyDataOffset.QuadPart = TC_BOOT_VOLUME_HEADER_SECTOR_OFFSET; - - ntStatus = ZwReadFile (hParentDeviceFile, - NULL, - NULL, - NULL, - &IoStatusBlock, - readBuffer, - max (TC_VOLUME_HEADER_EFFECTIVE_SIZE, Extension->HostBytesPerSector), - &parentKeyDataOffset, - NULL); - - if (hParentDeviceFile != NULL) - ZwClose (hParentDeviceFile); - } - - if (!NT_SUCCESS (ntStatus) && ntStatus != STATUS_END_OF_FILE) - { - Dump ("Read failed: NTSTATUS 0x%08x\n", ntStatus); - goto error; - } - - if (ntStatus == STATUS_END_OF_FILE || IoStatusBlock.Information < TC_VOLUME_HEADER_EFFECTIVE_SIZE) - { - Dump ("Read didn't read enough data\n"); - - // If FSCTL_ALLOW_EXTENDED_DASD_IO failed and there is a live filesystem on the partition, then the - // filesystem driver may report EOF when we are reading hidden sectors (when the filesystem is - // shorter than the partition). This can happen for example after the user quick-formats a dismounted - // partition-hosted TrueCrypt volume and then tries to mount the volume using the embedded backup header. - memset (readBuffer, 0, TC_VOLUME_HEADER_EFFECTIVE_SIZE); - } - - /* Attempt to recognize the volume (decrypt the header) */ - - ReadVolumeHeaderRecoveryMode = mount->RecoveryMode; - - if ((volumeType == TC_VOLUME_TYPE_HIDDEN) && mount->bProtectHiddenVolume) - { - mount->nReturnCode = ReadVolumeHeaderWCache ( - FALSE, - mount->bCache, - mount->bCachePim, - readBuffer, - &mount->ProtectedHidVolPassword, - mount->ProtectedHidVolPkcs5Prf, - mount->ProtectedHidVolPim, - mount->bTrueCryptMode, - &tmpCryptoInfo); - } - else - { - mount->nReturnCode = ReadVolumeHeaderWCache ( - mount->bPartitionInInactiveSysEncScope && volumeType == TC_VOLUME_TYPE_NORMAL, - mount->bCache, - mount->bCachePim, - readBuffer, - &mount->VolumePassword, - mount->pkcs5_prf, - mount->VolumePim, - mount->bTrueCryptMode, - &Extension->cryptoInfo); - } - - ReadVolumeHeaderRecoveryMode = FALSE; - - if (mount->nReturnCode == 0 || mount->nReturnCode == ERR_CIPHER_INIT_WEAK_KEY) - { - /* Volume header successfully decrypted */ - - if (!Extension->cryptoInfo) - { - /* should never happen */ - mount->nReturnCode = ERR_OUTOFMEMORY; - ntStatus = STATUS_SUCCESS; - goto error; - } - - Dump ("Volume header decrypted\n"); - Dump ("Required program version = %x\n", (int) Extension->cryptoInfo->RequiredProgramVersion); - Dump ("Legacy volume = %d\n", (int) Extension->cryptoInfo->LegacyVolume); - - if (IsHiddenSystemRunning() && !Extension->cryptoInfo->hiddenVolume) - { - Extension->bReadOnly = mount->bMountReadOnly = TRUE; - HiddenSysLeakProtectionCount++; - } - - Extension->cryptoInfo->bProtectHiddenVolume = FALSE; - Extension->cryptoInfo->bHiddenVolProtectionAction = FALSE; - - Extension->cryptoInfo->bPartitionInInactiveSysEncScope = mount->bPartitionInInactiveSysEncScope; - - /* compute the ID of this volume: SHA-512 of the effective header */ - sha256 (Extension->volumeID, readBuffer, TC_VOLUME_HEADER_EFFECTIVE_SIZE); - - if (volumeType == TC_VOLUME_TYPE_NORMAL) - { - if (mount->bPartitionInInactiveSysEncScope) - { - if (Extension->cryptoInfo->EncryptedAreaStart.Value > (unsigned __int64) partitionStartingOffset - || Extension->cryptoInfo->EncryptedAreaStart.Value + Extension->cryptoInfo->VolumeSize.Value <= (unsigned __int64) partitionStartingOffset) - { - // The partition is not within the key scope of system encryption - mount->nReturnCode = ERR_PASSWORD_WRONG; - ntStatus = STATUS_SUCCESS; - goto error; - } - - if (Extension->cryptoInfo->EncryptedAreaLength.Value != Extension->cryptoInfo->VolumeSize.Value) - { - // Partial encryption is not supported for volumes mounted as regular - mount->nReturnCode = ERR_ENCRYPTION_NOT_COMPLETED; - ntStatus = STATUS_SUCCESS; - goto error; - } - } - else if (Extension->cryptoInfo->HeaderFlags & TC_HEADER_FLAG_NONSYS_INPLACE_ENC) - { - if (Extension->cryptoInfo->EncryptedAreaLength.Value != Extension->cryptoInfo->VolumeSize.Value) - { - // Non-system in-place encryption process has not been completed on this volume - mount->nReturnCode = ERR_NONSYS_INPLACE_ENC_INCOMPLETE; - ntStatus = STATUS_SUCCESS; - goto error; - } - } - } - - Extension->cryptoInfo->FirstDataUnitNo.Value = 0; - - if (Extension->cryptoInfo->hiddenVolume && IsHiddenSystemRunning()) - { - // Prevent mount of a hidden system partition if the system hosted on it is currently running - if (memcmp (Extension->cryptoInfo->master_keydata, GetSystemDriveCryptoInfo()->master_keydata, EAGetKeySize (Extension->cryptoInfo->ea)) == 0) - { - mount->nReturnCode = ERR_VOL_ALREADY_MOUNTED; - ntStatus = STATUS_SUCCESS; - goto error; - } - } - - switch (volumeType) - { - case TC_VOLUME_TYPE_NORMAL: - - Extension->cryptoInfo->hiddenVolume = FALSE; - - if (mount->bPartitionInInactiveSysEncScope) - { - Extension->cryptoInfo->volDataAreaOffset = 0; - Extension->DiskLength = lDiskLength.QuadPart; - Extension->cryptoInfo->FirstDataUnitNo.Value = partitionStartingOffset / ENCRYPTION_DATA_UNIT_SIZE; - } - else if (Extension->cryptoInfo->LegacyVolume) - { - Extension->cryptoInfo->volDataAreaOffset = TC_VOLUME_HEADER_SIZE_LEGACY; - Extension->DiskLength = lDiskLength.QuadPart - TC_VOLUME_HEADER_SIZE_LEGACY; - } - else - { - Extension->cryptoInfo->volDataAreaOffset = Extension->cryptoInfo->EncryptedAreaStart.Value; - Extension->DiskLength = Extension->cryptoInfo->VolumeSize.Value; - } - - break; - - case TC_VOLUME_TYPE_HIDDEN: - - cryptoInfoPtr = mount->bProtectHiddenVolume ? tmpCryptoInfo : Extension->cryptoInfo; - - Extension->cryptoInfo->hiddenVolumeOffset = cryptoInfoPtr->EncryptedAreaStart.Value; - - Dump ("Hidden volume offset = %I64d\n", Extension->cryptoInfo->hiddenVolumeOffset); - Dump ("Hidden volume size = %I64d\n", cryptoInfoPtr->hiddenVolumeSize); - Dump ("Hidden volume end = %I64d\n", Extension->cryptoInfo->hiddenVolumeOffset + cryptoInfoPtr->hiddenVolumeSize - 1); - - // Validate the offset - if (Extension->cryptoInfo->hiddenVolumeOffset % ENCRYPTION_DATA_UNIT_SIZE != 0) - { - mount->nReturnCode = ERR_VOL_SIZE_WRONG; - ntStatus = STATUS_SUCCESS; - goto error; - } - - // If we are supposed to actually mount the hidden volume (not just to protect it) - if (!mount->bProtectHiddenVolume) - { - Extension->DiskLength = cryptoInfoPtr->hiddenVolumeSize; - Extension->cryptoInfo->hiddenVolume = TRUE; - Extension->cryptoInfo->volDataAreaOffset = Extension->cryptoInfo->hiddenVolumeOffset; - } - else - { - // Hidden volume protection - Extension->cryptoInfo->hiddenVolume = FALSE; - Extension->cryptoInfo->bProtectHiddenVolume = TRUE; - - Extension->cryptoInfo->hiddenVolumeProtectedSize = tmpCryptoInfo->hiddenVolumeSize; - - Dump ("Hidden volume protection active: %I64d-%I64d (%I64d)\n", Extension->cryptoInfo->hiddenVolumeOffset, Extension->cryptoInfo->hiddenVolumeProtectedSize + Extension->cryptoInfo->hiddenVolumeOffset - 1, Extension->cryptoInfo->hiddenVolumeProtectedSize); - } - - break; - } - - Dump ("Volume data offset = %I64d\n", Extension->cryptoInfo->volDataAreaOffset); - Dump ("Volume data size = %I64d\n", Extension->DiskLength); - Dump ("Volume data end = %I64d\n", Extension->cryptoInfo->volDataAreaOffset + Extension->DiskLength - 1); - - if (Extension->DiskLength == 0) - { - Dump ("Incorrect volume size\n"); - continue; - } - - // If this is a hidden volume, make sure we are supposed to actually - // mount it (i.e. not just to protect it) - if (volumeType == TC_VOLUME_TYPE_NORMAL || !mount->bProtectHiddenVolume) - { - // Validate sector size - if (bRawDevice && Extension->cryptoInfo->SectorSize != Extension->HostBytesPerSector) - { - mount->nReturnCode = ERR_PARAMETER_INCORRECT; - ntStatus = STATUS_SUCCESS; - goto error; - } - - // Calculate virtual volume geometry - Extension->TracksPerCylinder = 1; - Extension->SectorsPerTrack = 1; - Extension->BytesPerSector = Extension->cryptoInfo->SectorSize; - Extension->NumberOfCylinders = Extension->DiskLength / Extension->BytesPerSector; - Extension->PartitionType = 0; - - Extension->bRawDevice = bRawDevice; - - memset (Extension->wszVolume, 0, sizeof (Extension->wszVolume)); - if (wcsstr (pwszMountVolume, WIDE ("\\??\\UNC\\")) == pwszMountVolume) - { - /* UNC path */ - RtlStringCbPrintfW (Extension->wszVolume, - sizeof (Extension->wszVolume), - WIDE ("\\??\\\\%s"), - pwszMountVolume + 7); - } - else - { - RtlStringCbCopyW (Extension->wszVolume, sizeof(Extension->wszVolume),pwszMountVolume); - } - - memset (Extension->wszLabel, 0, sizeof (Extension->wszLabel)); - RtlStringCbCopyW (Extension->wszLabel, sizeof(Extension->wszLabel), mount->wszLabel); - } - - // If we are to protect a hidden volume we cannot exit yet, for we must also - // decrypt the hidden volume header. - if (!(volumeType == TC_VOLUME_TYPE_NORMAL && mount->bProtectHiddenVolume)) - { - TCfree (readBuffer); - - if (tmpCryptoInfo != NULL) - { - crypto_close (tmpCryptoInfo); - tmpCryptoInfo = NULL; - } - - return STATUS_SUCCESS; - } - } - else if ((mount->bProtectHiddenVolume && volumeType == TC_VOLUME_TYPE_NORMAL) - || mount->nReturnCode != ERR_PASSWORD_WRONG) - { - /* If we are not supposed to protect a hidden volume, the only error that is - tolerated is ERR_PASSWORD_WRONG (to allow mounting a possible hidden volume). - - If we _are_ supposed to protect a hidden volume, we do not tolerate any error - (both volume headers must be successfully decrypted). */ - - break; - } - } - - /* Failed due to some non-OS reason so we drop through and return NT - SUCCESS then nReturnCode is checked later in user-mode */ - - if (mount->nReturnCode == ERR_OUTOFMEMORY) - ntStatus = STATUS_INSUFFICIENT_RESOURCES; - else - ntStatus = STATUS_SUCCESS; - -error: - if (mount->nReturnCode == ERR_SUCCESS) - mount->nReturnCode = ERR_PASSWORD_WRONG; - - if (tmpCryptoInfo != NULL) - { - crypto_close (tmpCryptoInfo); - tmpCryptoInfo = NULL; - } - - if (Extension->cryptoInfo) - { - crypto_close (Extension->cryptoInfo); - Extension->cryptoInfo = NULL; - } - - if (Extension->bTimeStampValid) - { - RestoreTimeStamp (Extension); - } - - /* Close the hDeviceFile */ - if (Extension->hDeviceFile != NULL) - ZwClose (Extension->hDeviceFile); - - /* The cryptoInfo pointer is deallocated if the readheader routines - fail so there is no need to deallocate here */ - - /* Dereference the user-mode file object */ - if (Extension->pfoDeviceFile != NULL) - ObDereferenceObject (Extension->pfoDeviceFile); - - /* Free the tmp IO buffers */ - if (readBuffer != NULL) - TCfree (readBuffer); - - return ntStatus; -} - -void TCCloseVolume (PDEVICE_OBJECT DeviceObject, PEXTENSION Extension) -{ - UNREFERENCED_PARAMETER (DeviceObject); /* Remove compiler warning */ - - if (Extension->hDeviceFile != NULL) - { - if (Extension->bRawDevice == FALSE - && Extension->bTimeStampValid) - { - RestoreTimeStamp (Extension); - } - ZwClose (Extension->hDeviceFile); - } - ObDereferenceObject (Extension->pfoDeviceFile); - if (Extension->cryptoInfo) - { - crypto_close (Extension->cryptoInfo); - Extension->cryptoInfo = NULL; - } -} - - -NTSTATUS TCSendHostDeviceIoControlRequestEx (PDEVICE_OBJECT DeviceObject, - PEXTENSION Extension, - ULONG IoControlCode, - void *InputBuffer, - ULONG InputBufferSize, - void *OutputBuffer, - ULONG OutputBufferSize) -{ - IO_STATUS_BLOCK IoStatusBlock; - NTSTATUS ntStatus; - PIRP Irp; - - UNREFERENCED_PARAMETER(DeviceObject); /* Remove compiler warning */ - - KeClearEvent (&Extension->keVolumeEvent); - - Irp = IoBuildDeviceIoControlRequest (IoControlCode, - Extension->pFsdDevice, - InputBuffer, InputBufferSize, - OutputBuffer, OutputBufferSize, - FALSE, - &Extension->keVolumeEvent, - &IoStatusBlock); - - if (Irp == NULL) - { - Dump ("IRP allocation failed\n"); - return STATUS_INSUFFICIENT_RESOURCES; - } - - // Disk device may be used by filesystem driver which needs file object - IoGetNextIrpStackLocation (Irp) -> FileObject = Extension->pfoDeviceFile; - - ntStatus = IoCallDriver (Extension->pFsdDevice, Irp); - if (ntStatus == STATUS_PENDING) - { - KeWaitForSingleObject (&Extension->keVolumeEvent, Executive, KernelMode, FALSE, NULL); - ntStatus = IoStatusBlock.Status; - } - - return ntStatus; -} - -NTSTATUS TCSendHostDeviceIoControlRequest (PDEVICE_OBJECT DeviceObject, - PEXTENSION Extension, - ULONG IoControlCode, - void *OutputBuffer, - ULONG OutputBufferSize) -{ - return TCSendHostDeviceIoControlRequestEx (DeviceObject, Extension, IoControlCode, NULL, 0, OutputBuffer, OutputBufferSize); -} - -NTSTATUS COMPLETE_IRP (PDEVICE_OBJECT DeviceObject, - PIRP Irp, - NTSTATUS IrpStatus, - ULONG_PTR IrpInformation) -{ - Irp->IoStatus.Status = IrpStatus; - Irp->IoStatus.Information = IrpInformation; - - UNREFERENCED_PARAMETER (DeviceObject); /* Remove compiler warning */ - -#if EXTRA_INFO - if (!NT_SUCCESS (IrpStatus)) - { - PIO_STACK_LOCATION irpSp = IoGetCurrentIrpStackLocation (Irp); - Dump ("COMPLETE_IRP FAILING IRP %ls Flags 0x%08x vpb 0x%08x NTSTATUS 0x%08x\n", TCTranslateCode (irpSp->MajorFunction), - (ULONG) DeviceObject->Flags, (ULONG) DeviceObject->Vpb->Flags, IrpStatus); - } - else - { - PIO_STACK_LOCATION irpSp = IoGetCurrentIrpStackLocation (Irp); - Dump ("COMPLETE_IRP SUCCESS IRP %ls Flags 0x%08x vpb 0x%08x NTSTATUS 0x%08x\n", TCTranslateCode (irpSp->MajorFunction), - (ULONG) DeviceObject->Flags, (ULONG) DeviceObject->Vpb->Flags, IrpStatus); - } -#endif - IoCompleteRequest (Irp, IO_NO_INCREMENT); - return IrpStatus; -} - - -static void RestoreTimeStamp (PEXTENSION Extension) -{ - NTSTATUS ntStatus; - FILE_BASIC_INFORMATION FileBasicInfo; - IO_STATUS_BLOCK IoStatusBlock; - - if (Extension->hDeviceFile != NULL - && Extension->bRawDevice == FALSE - && Extension->bReadOnly == FALSE - && Extension->bTimeStampValid) - { - ntStatus = ZwQueryInformationFile (Extension->hDeviceFile, - &IoStatusBlock, - &FileBasicInfo, - sizeof (FileBasicInfo), - FileBasicInformation); - - if (!NT_SUCCESS (ntStatus)) - { - Dump ("ZwQueryInformationFile failed in RestoreTimeStamp: NTSTATUS 0x%08x\n", - ntStatus); - } - else - { - FileBasicInfo.CreationTime = Extension->fileCreationTime; - FileBasicInfo.LastAccessTime = Extension->fileLastAccessTime; - FileBasicInfo.LastWriteTime = Extension->fileLastWriteTime; - FileBasicInfo.ChangeTime = Extension->fileLastChangeTime; - - ntStatus = ZwSetInformationFile( - Extension->hDeviceFile, - &IoStatusBlock, - &FileBasicInfo, - sizeof (FileBasicInfo), - FileBasicInformation); - - if (!NT_SUCCESS (ntStatus)) - Dump ("ZwSetInformationFile failed in RestoreTimeStamp: NTSTATUS 0x%08x\n",ntStatus); - } - } -} +/* + Legal Notice: Some portions of the source code contained in this file were + derived from the source code of TrueCrypt 7.1a, which is + Copyright (c) 2003-2012 TrueCrypt Developers Association and which is + governed by the TrueCrypt License 3.0, also from the source code of + Encryption for the Masses 2.02a, which is Copyright (c) 1998-2000 Paul Le Roux + and which is governed by the 'License Agreement for Encryption for the Masses' + Modifications and additions to the original source code (contained in this file) + and all other portions of this file are Copyright (c) 2013-2016 IDRIX + and are governed by the Apache License 2.0 the full text of which is + contained in the file License.txt included in VeraCrypt binary and source + code distribution packages. */ + +#include "TCdefs.h" +#include +#include "Crypto.h" +#include "Volumes.h" + +#include "Apidrvr.h" +#include "DriveFilter.h" +#include "Ntdriver.h" +#include "Ntvol.h" +#include "VolumeFilter.h" + +#include "Boot/Windows/BootCommon.h" + +#include "Cache.h" + +#if 0 && _DEBUG +#define EXTRA_INFO 1 +#endif + +#pragma warning( disable : 4127 ) + +#include + +volatile BOOL ProbingHostDeviceForWrite = FALSE; + + +NTSTATUS TCOpenVolume (PDEVICE_OBJECT DeviceObject, + PEXTENSION Extension, + MOUNT_STRUCT *mount, + PWSTR pwszMountVolume, + BOOL bRawDevice) +{ + FILE_STANDARD_INFORMATION FileStandardInfo; + FILE_BASIC_INFORMATION FileBasicInfo; + OBJECT_ATTRIBUTES oaFileAttributes; + UNICODE_STRING FullFileName; + IO_STATUS_BLOCK IoStatusBlock; + PCRYPTO_INFO cryptoInfoPtr = NULL; + PCRYPTO_INFO tmpCryptoInfo = NULL; + LARGE_INTEGER lDiskLength; + __int64 partitionStartingOffset = 0; + int volumeType; + char *readBuffer = 0; + NTSTATUS ntStatus = 0; + BOOL forceAccessCheck = (!bRawDevice && !(OsMajorVersion == 5 &&OsMinorVersion == 0)); // Windows 2000 does not support OBJ_FORCE_ACCESS_CHECK attribute + BOOL disableBuffering = TRUE; + BOOL exclusiveAccess = mount->bExclusiveAccess; + + Extension->pfoDeviceFile = NULL; + Extension->hDeviceFile = NULL; + Extension->bTimeStampValid = FALSE; + + RtlInitUnicodeString (&FullFileName, pwszMountVolume); + InitializeObjectAttributes (&oaFileAttributes, &FullFileName, OBJ_CASE_INSENSITIVE | (forceAccessCheck ? OBJ_FORCE_ACCESS_CHECK : 0) | OBJ_KERNEL_HANDLE, NULL, NULL); + KeInitializeEvent (&Extension->keVolumeEvent, NotificationEvent, FALSE); + + if (Extension->SecurityClientContextValid) + { + ntStatus = SeImpersonateClientEx (&Extension->SecurityClientContext, NULL); + if (!NT_SUCCESS (ntStatus)) + goto error; + } + + mount->VolumeMountedReadOnlyAfterDeviceWriteProtected = FALSE; + + // If we are opening a device, query its size first + if (bRawDevice) + { + PARTITION_INFORMATION pi; + PARTITION_INFORMATION_EX pix; + LARGE_INTEGER diskLengthInfo; + DISK_GEOMETRY dg; + STORAGE_PROPERTY_QUERY storagePropertyQuery = {0}; + STORAGE_ACCESS_ALIGNMENT_DESCRIPTOR storageDescriptor = {0}; + + ntStatus = IoGetDeviceObjectPointer (&FullFileName, + FILE_READ_DATA | FILE_READ_ATTRIBUTES, + &Extension->pfoDeviceFile, + &Extension->pFsdDevice); + + if (!NT_SUCCESS (ntStatus)) + goto error; + + ntStatus = TCSendHostDeviceIoControlRequest (DeviceObject, Extension, IOCTL_DISK_GET_DRIVE_GEOMETRY, (char *) &dg, sizeof (dg)); + if (!NT_SUCCESS (ntStatus)) + goto error; + + lDiskLength.QuadPart = dg.Cylinders.QuadPart * dg.SectorsPerTrack * dg.TracksPerCylinder * dg.BytesPerSector; + Extension->HostBytesPerSector = dg.BytesPerSector; + + storagePropertyQuery.PropertyId = StorageAccessAlignmentProperty; + storagePropertyQuery.QueryType = PropertyStandardQuery; + + /* IOCTL_STORAGE_QUERY_PROPERTY supported only on Vista and above */ + if (NT_SUCCESS (TCSendHostDeviceIoControlRequestEx (DeviceObject, Extension, IOCTL_STORAGE_QUERY_PROPERTY, + (char*) &storagePropertyQuery, sizeof(storagePropertyQuery), + (char *) &storageDescriptor, sizeof (storageDescriptor)))) + { + Extension->HostBytesPerPhysicalSector = storageDescriptor.BytesPerPhysicalSector; + } + else + { + Extension->HostBytesPerPhysicalSector = dg.BytesPerSector; + } + + // Drive geometry is used only when IOCTL_DISK_GET_PARTITION_INFO fails + if (NT_SUCCESS (TCSendHostDeviceIoControlRequest (DeviceObject, Extension, IOCTL_DISK_GET_PARTITION_INFO_EX, (char *) &pix, sizeof (pix)))) + { + lDiskLength.QuadPart = pix.PartitionLength.QuadPart; + partitionStartingOffset = pix.StartingOffset.QuadPart; + } + // Windows 2000 does not support IOCTL_DISK_GET_PARTITION_INFO_EX + else if (NT_SUCCESS (TCSendHostDeviceIoControlRequest (DeviceObject, Extension, IOCTL_DISK_GET_PARTITION_INFO, (char *) &pi, sizeof (pi)))) + { + lDiskLength.QuadPart = pi.PartitionLength.QuadPart; + partitionStartingOffset = pi.StartingOffset.QuadPart; + } + else if (NT_SUCCESS (TCSendHostDeviceIoControlRequest (DeviceObject, Extension, IOCTL_DISK_GET_LENGTH_INFO, &diskLengthInfo, sizeof (diskLengthInfo)))) + { + lDiskLength = diskLengthInfo; + } + + ProbingHostDeviceForWrite = TRUE; + + if (!mount->bMountReadOnly + && TCSendHostDeviceIoControlRequest (DeviceObject, Extension, + IsHiddenSystemRunning() ? TC_IOCTL_DISK_IS_WRITABLE : IOCTL_DISK_IS_WRITABLE, NULL, 0) == STATUS_MEDIA_WRITE_PROTECTED) + { + mount->bMountReadOnly = TRUE; + DeviceObject->Characteristics |= FILE_READ_ONLY_DEVICE; + mount->VolumeMountedReadOnlyAfterDeviceWriteProtected = TRUE; + } + + ProbingHostDeviceForWrite = FALSE; + + // Some Windows tools (e.g. diskmgmt, diskpart, vssadmin) fail or experience timeouts when there is a raw device + // open for exclusive access. Therefore, exclusive access is used only for file-hosted volumes. + // Applications requiring a consistent device image need to acquire exclusive write access first. This is prevented + // when a device-hosted volume is mounted. + + exclusiveAccess = FALSE; + } + else + { + // Limit the maximum required buffer size + if (mount->BytesPerSector > 128 * BYTES_PER_KB) + { + ntStatus = STATUS_INVALID_PARAMETER; + goto error; + } + + Extension->HostBytesPerSector = mount->BytesPerSector; + Extension->HostBytesPerPhysicalSector = mount->BytesPerPhysicalSector; + + if (Extension->HostBytesPerSector != TC_SECTOR_SIZE_FILE_HOSTED_VOLUME) + disableBuffering = FALSE; + } + + // Open the volume hosting file/device + if (!mount->bMountReadOnly) + { + ntStatus = ZwCreateFile (&Extension->hDeviceFile, + GENERIC_READ | GENERIC_WRITE | SYNCHRONIZE, + &oaFileAttributes, + &IoStatusBlock, + NULL, + FILE_ATTRIBUTE_NORMAL | + FILE_ATTRIBUTE_SYSTEM, + exclusiveAccess ? 0 : FILE_SHARE_READ | FILE_SHARE_WRITE, + FILE_OPEN, + FILE_RANDOM_ACCESS | + FILE_WRITE_THROUGH | + (disableBuffering ? FILE_NO_INTERMEDIATE_BUFFERING : 0) | + FILE_SYNCHRONOUS_IO_NONALERT, + NULL, + 0); + } + + /* 26-4-99 NT for some partitions returns this code, it is really a access denied */ + if (ntStatus == 0xc000001b) + ntStatus = STATUS_ACCESS_DENIED; + + mount->VolumeMountedReadOnlyAfterAccessDenied = FALSE; + + if (mount->bMountReadOnly || ntStatus == STATUS_ACCESS_DENIED) + { + ntStatus = ZwCreateFile (&Extension->hDeviceFile, + GENERIC_READ | SYNCHRONIZE, + &oaFileAttributes, + &IoStatusBlock, + NULL, + FILE_ATTRIBUTE_NORMAL | + FILE_ATTRIBUTE_SYSTEM, + exclusiveAccess ? FILE_SHARE_READ : FILE_SHARE_READ | FILE_SHARE_WRITE, + FILE_OPEN, + FILE_RANDOM_ACCESS | + FILE_WRITE_THROUGH | + (disableBuffering ? FILE_NO_INTERMEDIATE_BUFFERING : 0) | + FILE_SYNCHRONOUS_IO_NONALERT, + NULL, + 0); + + if (NT_SUCCESS (ntStatus) && !mount->bMountReadOnly) + mount->VolumeMountedReadOnlyAfterAccessDenied = TRUE; + + Extension->bReadOnly = TRUE; + DeviceObject->Characteristics |= FILE_READ_ONLY_DEVICE; + } + else + Extension->bReadOnly = FALSE; + + /* 26-4-99 NT for some partitions returns this code, it is really a + access denied */ + if (ntStatus == 0xc000001b) + { + /* Partitions which return this code can still be opened with + FILE_SHARE_READ but this causes NT problems elsewhere in + particular if you do FILE_SHARE_READ NT will die later if + anyone even tries to open the partition (or file for that + matter...) */ + ntStatus = STATUS_SHARING_VIOLATION; + } + + if (!NT_SUCCESS (ntStatus)) + { + goto error; + } + + // If we have opened a file, query its size now + if (bRawDevice == FALSE) + { + ntStatus = ZwQueryInformationFile (Extension->hDeviceFile, + &IoStatusBlock, + &FileBasicInfo, + sizeof (FileBasicInfo), + FileBasicInformation); + + if (NT_SUCCESS (ntStatus)) + { + if (mount->bPreserveTimestamp) + { + Extension->fileCreationTime = FileBasicInfo.CreationTime; + Extension->fileLastAccessTime = FileBasicInfo.LastAccessTime; + Extension->fileLastWriteTime = FileBasicInfo.LastWriteTime; + Extension->fileLastChangeTime = FileBasicInfo.ChangeTime; + Extension->bTimeStampValid = TRUE; + } + + ntStatus = ZwQueryInformationFile (Extension->hDeviceFile, + &IoStatusBlock, + &FileStandardInfo, + sizeof (FileStandardInfo), + FileStandardInformation); + } + + if (!NT_SUCCESS (ntStatus)) + { + Dump ("ZwQueryInformationFile failed while opening file: NTSTATUS 0x%08x\n", + ntStatus); + goto error; + } + + lDiskLength.QuadPart = FileStandardInfo.EndOfFile.QuadPart; + + if (FileBasicInfo.FileAttributes & FILE_ATTRIBUTE_COMPRESSED) + { + Dump ("File \"%ls\" is marked as compressed - not supported!\n", pwszMountVolume); + mount->nReturnCode = ERR_COMPRESSION_NOT_SUPPORTED; + ntStatus = STATUS_SUCCESS; + goto error; + } + + ntStatus = ObReferenceObjectByHandle (Extension->hDeviceFile, + FILE_ALL_ACCESS, + *IoFileObjectType, + KernelMode, + &Extension->pfoDeviceFile, + 0); + + if (!NT_SUCCESS (ntStatus)) + { + goto error; + } + + /* Get the FSD device for the file (probably either NTFS or FAT) */ + Extension->pFsdDevice = IoGetRelatedDeviceObject (Extension->pfoDeviceFile); + } + else + { + // Try to gain "raw" access to the partition in case there is a live filesystem on it (otherwise, + // the NTFS driver guards hidden sectors and prevents mounting using a backup header e.g. after the user + // accidentally quick-formats a dismounted partition-hosted TrueCrypt volume as NTFS). + + PFILE_OBJECT pfoTmpDeviceFile = NULL; + + if (NT_SUCCESS (ObReferenceObjectByHandle (Extension->hDeviceFile, FILE_ALL_ACCESS, *IoFileObjectType, KernelMode, &pfoTmpDeviceFile, NULL)) + && pfoTmpDeviceFile != NULL) + { + TCFsctlCall (pfoTmpDeviceFile, FSCTL_ALLOW_EXTENDED_DASD_IO, NULL, 0, NULL, 0); + ObDereferenceObject (pfoTmpDeviceFile); + } + } + + // Check volume size + if (lDiskLength.QuadPart < TC_MIN_VOLUME_SIZE_LEGACY || lDiskLength.QuadPart > TC_MAX_VOLUME_SIZE) + { + mount->nReturnCode = ERR_VOL_SIZE_WRONG; + ntStatus = STATUS_SUCCESS; + goto error; + } + + Extension->DiskLength = lDiskLength.QuadPart; + Extension->HostLength = lDiskLength.QuadPart; + + readBuffer = TCalloc (max (max (TC_VOLUME_HEADER_EFFECTIVE_SIZE, PAGE_SIZE), Extension->HostBytesPerSector)); + if (readBuffer == NULL) + { + ntStatus = STATUS_INSUFFICIENT_RESOURCES; + goto error; + } + + // Go through all volume types (e.g., normal, hidden) + for (volumeType = TC_VOLUME_TYPE_NORMAL; + volumeType < TC_VOLUME_TYPE_COUNT; + volumeType++) + { + Dump ("Trying to open volume type %d\n", volumeType); + + /* Read the volume header */ + + if (!mount->bPartitionInInactiveSysEncScope + || (mount->bPartitionInInactiveSysEncScope && volumeType == TC_VOLUME_TYPE_HIDDEN)) + { + // Header of a volume that is not within the scope of system encryption, or + // header of a system hidden volume (containing a hidden OS) + + LARGE_INTEGER headerOffset; + + if (mount->UseBackupHeader && lDiskLength.QuadPart <= TC_TOTAL_VOLUME_HEADERS_SIZE) + continue; + + switch (volumeType) + { + case TC_VOLUME_TYPE_NORMAL: + headerOffset.QuadPart = mount->UseBackupHeader ? lDiskLength.QuadPart - TC_VOLUME_HEADER_GROUP_SIZE : TC_VOLUME_HEADER_OFFSET; + break; + + case TC_VOLUME_TYPE_HIDDEN: + if (lDiskLength.QuadPart <= TC_VOLUME_HEADER_GROUP_SIZE) + continue; + + headerOffset.QuadPart = mount->UseBackupHeader ? lDiskLength.QuadPart - TC_HIDDEN_VOLUME_HEADER_OFFSET : TC_HIDDEN_VOLUME_HEADER_OFFSET; + break; + } + + Dump ("Reading volume header at %I64d\n", headerOffset.QuadPart); + + ntStatus = ZwReadFile (Extension->hDeviceFile, + NULL, + NULL, + NULL, + &IoStatusBlock, + readBuffer, + bRawDevice ? max (TC_VOLUME_HEADER_EFFECTIVE_SIZE, Extension->HostBytesPerSector) : TC_VOLUME_HEADER_EFFECTIVE_SIZE, + &headerOffset, + NULL); + } + else + { + // Header of a partition that is within the scope of system encryption + + WCHAR parentDrivePath [47+1] = {0}; + HANDLE hParentDeviceFile = NULL; + UNICODE_STRING FullParentPath; + OBJECT_ATTRIBUTES oaParentFileAttributes; + LARGE_INTEGER parentKeyDataOffset; + + RtlStringCbPrintfW (parentDrivePath, + sizeof (parentDrivePath), + WIDE ("\\Device\\Harddisk%d\\Partition0"), + mount->nPartitionInInactiveSysEncScopeDriveNo); + + Dump ("Mounting partition within scope of system encryption (reading key data from: %ls)\n", parentDrivePath); + + RtlInitUnicodeString (&FullParentPath, parentDrivePath); + InitializeObjectAttributes (&oaParentFileAttributes, &FullParentPath, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, NULL, NULL); + + ntStatus = ZwCreateFile (&hParentDeviceFile, + GENERIC_READ | SYNCHRONIZE, + &oaParentFileAttributes, + &IoStatusBlock, + NULL, + FILE_ATTRIBUTE_NORMAL | + FILE_ATTRIBUTE_SYSTEM, + FILE_SHARE_READ | FILE_SHARE_WRITE, + FILE_OPEN, + FILE_RANDOM_ACCESS | + FILE_WRITE_THROUGH | + FILE_NO_INTERMEDIATE_BUFFERING | + FILE_SYNCHRONOUS_IO_NONALERT, + NULL, + 0); + + if (!NT_SUCCESS (ntStatus)) + { + if (hParentDeviceFile != NULL) + ZwClose (hParentDeviceFile); + + Dump ("Cannot open %ls\n", parentDrivePath); + + goto error; + } + + parentKeyDataOffset.QuadPart = TC_BOOT_VOLUME_HEADER_SECTOR_OFFSET; + + ntStatus = ZwReadFile (hParentDeviceFile, + NULL, + NULL, + NULL, + &IoStatusBlock, + readBuffer, + max (TC_VOLUME_HEADER_EFFECTIVE_SIZE, Extension->HostBytesPerSector), + &parentKeyDataOffset, + NULL); + + if (hParentDeviceFile != NULL) + ZwClose (hParentDeviceFile); + } + + if (!NT_SUCCESS (ntStatus) && ntStatus != STATUS_END_OF_FILE) + { + Dump ("Read failed: NTSTATUS 0x%08x\n", ntStatus); + goto error; + } + + if (ntStatus == STATUS_END_OF_FILE || IoStatusBlock.Information < TC_VOLUME_HEADER_EFFECTIVE_SIZE) + { + Dump ("Read didn't read enough data\n"); + + // If FSCTL_ALLOW_EXTENDED_DASD_IO failed and there is a live filesystem on the partition, then the + // filesystem driver may report EOF when we are reading hidden sectors (when the filesystem is + // shorter than the partition). This can happen for example after the user quick-formats a dismounted + // partition-hosted TrueCrypt volume and then tries to mount the volume using the embedded backup header. + memset (readBuffer, 0, TC_VOLUME_HEADER_EFFECTIVE_SIZE); + } + + /* Attempt to recognize the volume (decrypt the header) */ + + ReadVolumeHeaderRecoveryMode = mount->RecoveryMode; + + if ((volumeType == TC_VOLUME_TYPE_HIDDEN) && mount->bProtectHiddenVolume) + { + mount->nReturnCode = ReadVolumeHeaderWCache ( + FALSE, + mount->bCache, + mount->bCachePim, + readBuffer, + &mount->ProtectedHidVolPassword, + mount->ProtectedHidVolPkcs5Prf, + mount->ProtectedHidVolPim, + mount->bTrueCryptMode, + &tmpCryptoInfo); + } + else + { + mount->nReturnCode = ReadVolumeHeaderWCache ( + mount->bPartitionInInactiveSysEncScope && volumeType == TC_VOLUME_TYPE_NORMAL, + mount->bCache, + mount->bCachePim, + readBuffer, + &mount->VolumePassword, + mount->pkcs5_prf, + mount->VolumePim, + mount->bTrueCryptMode, + &Extension->cryptoInfo); + } + + ReadVolumeHeaderRecoveryMode = FALSE; + + if (mount->nReturnCode == 0 || mount->nReturnCode == ERR_CIPHER_INIT_WEAK_KEY) + { + /* Volume header successfully decrypted */ + + if (!Extension->cryptoInfo) + { + /* should never happen */ + mount->nReturnCode = ERR_OUTOFMEMORY; + ntStatus = STATUS_SUCCESS; + goto error; + } + + Dump ("Volume header decrypted\n"); + Dump ("Required program version = %x\n", (int) Extension->cryptoInfo->RequiredProgramVersion); + Dump ("Legacy volume = %d\n", (int) Extension->cryptoInfo->LegacyVolume); + + if (IsHiddenSystemRunning() && !Extension->cryptoInfo->hiddenVolume) + { + Extension->bReadOnly = mount->bMountReadOnly = TRUE; + HiddenSysLeakProtectionCount++; + } + + Extension->cryptoInfo->bProtectHiddenVolume = FALSE; + Extension->cryptoInfo->bHiddenVolProtectionAction = FALSE; + + Extension->cryptoInfo->bPartitionInInactiveSysEncScope = mount->bPartitionInInactiveSysEncScope; + + /* compute the ID of this volume: SHA-512 of the effective header */ + sha256 (Extension->volumeID, readBuffer, TC_VOLUME_HEADER_EFFECTIVE_SIZE); + + if (volumeType == TC_VOLUME_TYPE_NORMAL) + { + if (mount->bPartitionInInactiveSysEncScope) + { + if (Extension->cryptoInfo->EncryptedAreaStart.Value > (unsigned __int64) partitionStartingOffset + || Extension->cryptoInfo->EncryptedAreaStart.Value + Extension->cryptoInfo->VolumeSize.Value <= (unsigned __int64) partitionStartingOffset) + { + // The partition is not within the key scope of system encryption + mount->nReturnCode = ERR_PASSWORD_WRONG; + ntStatus = STATUS_SUCCESS; + goto error; + } + + if (Extension->cryptoInfo->EncryptedAreaLength.Value != Extension->cryptoInfo->VolumeSize.Value) + { + // Partial encryption is not supported for volumes mounted as regular + mount->nReturnCode = ERR_ENCRYPTION_NOT_COMPLETED; + ntStatus = STATUS_SUCCESS; + goto error; + } + } + else if (Extension->cryptoInfo->HeaderFlags & TC_HEADER_FLAG_NONSYS_INPLACE_ENC) + { + if (Extension->cryptoInfo->EncryptedAreaLength.Value != Extension->cryptoInfo->VolumeSize.Value) + { + // Non-system in-place encryption process has not been completed on this volume + mount->nReturnCode = ERR_NONSYS_INPLACE_ENC_INCOMPLETE; + ntStatus = STATUS_SUCCESS; + goto error; + } + } + } + + Extension->cryptoInfo->FirstDataUnitNo.Value = 0; + + if (Extension->cryptoInfo->hiddenVolume && IsHiddenSystemRunning()) + { + // Prevent mount of a hidden system partition if the system hosted on it is currently running + if (memcmp (Extension->cryptoInfo->master_keydata, GetSystemDriveCryptoInfo()->master_keydata, EAGetKeySize (Extension->cryptoInfo->ea)) == 0) + { + mount->nReturnCode = ERR_VOL_ALREADY_MOUNTED; + ntStatus = STATUS_SUCCESS; + goto error; + } + } + + switch (volumeType) + { + case TC_VOLUME_TYPE_NORMAL: + + Extension->cryptoInfo->hiddenVolume = FALSE; + + if (mount->bPartitionInInactiveSysEncScope) + { + Extension->cryptoInfo->volDataAreaOffset = 0; + Extension->DiskLength = lDiskLength.QuadPart; + Extension->cryptoInfo->FirstDataUnitNo.Value = partitionStartingOffset / ENCRYPTION_DATA_UNIT_SIZE; + } + else if (Extension->cryptoInfo->LegacyVolume) + { + Extension->cryptoInfo->volDataAreaOffset = TC_VOLUME_HEADER_SIZE_LEGACY; + Extension->DiskLength = lDiskLength.QuadPart - TC_VOLUME_HEADER_SIZE_LEGACY; + } + else + { + Extension->cryptoInfo->volDataAreaOffset = Extension->cryptoInfo->EncryptedAreaStart.Value; + Extension->DiskLength = Extension->cryptoInfo->VolumeSize.Value; + } + + break; + + case TC_VOLUME_TYPE_HIDDEN: + + cryptoInfoPtr = mount->bProtectHiddenVolume ? tmpCryptoInfo : Extension->cryptoInfo; + + Extension->cryptoInfo->hiddenVolumeOffset = cryptoInfoPtr->EncryptedAreaStart.Value; + + Dump ("Hidden volume offset = %I64d\n", Extension->cryptoInfo->hiddenVolumeOffset); + Dump ("Hidden volume size = %I64d\n", cryptoInfoPtr->hiddenVolumeSize); + Dump ("Hidden volume end = %I64d\n", Extension->cryptoInfo->hiddenVolumeOffset + cryptoInfoPtr->hiddenVolumeSize - 1); + + // Validate the offset + if (Extension->cryptoInfo->hiddenVolumeOffset % ENCRYPTION_DATA_UNIT_SIZE != 0) + { + mount->nReturnCode = ERR_VOL_SIZE_WRONG; + ntStatus = STATUS_SUCCESS; + goto error; + } + + // If we are supposed to actually mount the hidden volume (not just to protect it) + if (!mount->bProtectHiddenVolume) + { + Extension->DiskLength = cryptoInfoPtr->hiddenVolumeSize; + Extension->cryptoInfo->hiddenVolume = TRUE; + Extension->cryptoInfo->volDataAreaOffset = Extension->cryptoInfo->hiddenVolumeOffset; + } + else + { + // Hidden volume protection + Extension->cryptoInfo->hiddenVolume = FALSE; + Extension->cryptoInfo->bProtectHiddenVolume = TRUE; + + Extension->cryptoInfo->hiddenVolumeProtectedSize = tmpCryptoInfo->hiddenVolumeSize; + + Dump ("Hidden volume protection active: %I64d-%I64d (%I64d)\n", Extension->cryptoInfo->hiddenVolumeOffset, Extension->cryptoInfo->hiddenVolumeProtectedSize + Extension->cryptoInfo->hiddenVolumeOffset - 1, Extension->cryptoInfo->hiddenVolumeProtectedSize); + } + + break; + } + + Dump ("Volume data offset = %I64d\n", Extension->cryptoInfo->volDataAreaOffset); + Dump ("Volume data size = %I64d\n", Extension->DiskLength); + Dump ("Volume data end = %I64d\n", Extension->cryptoInfo->volDataAreaOffset + Extension->DiskLength - 1); + + if (Extension->DiskLength == 0) + { + Dump ("Incorrect volume size\n"); + continue; + } + + // If this is a hidden volume, make sure we are supposed to actually + // mount it (i.e. not just to protect it) + if (volumeType == TC_VOLUME_TYPE_NORMAL || !mount->bProtectHiddenVolume) + { + // Validate sector size + if (bRawDevice && Extension->cryptoInfo->SectorSize != Extension->HostBytesPerSector) + { + mount->nReturnCode = ERR_PARAMETER_INCORRECT; + ntStatus = STATUS_SUCCESS; + goto error; + } + + // Calculate virtual volume geometry + Extension->TracksPerCylinder = 1; + Extension->SectorsPerTrack = 1; + Extension->BytesPerSector = Extension->cryptoInfo->SectorSize; + Extension->NumberOfCylinders = Extension->DiskLength / Extension->BytesPerSector; + Extension->PartitionType = 0; + + Extension->bRawDevice = bRawDevice; + + memset (Extension->wszVolume, 0, sizeof (Extension->wszVolume)); + if (wcsstr (pwszMountVolume, WIDE ("\\??\\UNC\\")) == pwszMountVolume) + { + /* UNC path */ + RtlStringCbPrintfW (Extension->wszVolume, + sizeof (Extension->wszVolume), + WIDE ("\\??\\\\%s"), + pwszMountVolume + 7); + } + else + { + RtlStringCbCopyW (Extension->wszVolume, sizeof(Extension->wszVolume),pwszMountVolume); + } + + memset (Extension->wszLabel, 0, sizeof (Extension->wszLabel)); + RtlStringCbCopyW (Extension->wszLabel, sizeof(Extension->wszLabel), mount->wszLabel); + } + + // If we are to protect a hidden volume we cannot exit yet, for we must also + // decrypt the hidden volume header. + if (!(volumeType == TC_VOLUME_TYPE_NORMAL && mount->bProtectHiddenVolume)) + { + TCfree (readBuffer); + + if (tmpCryptoInfo != NULL) + { + crypto_close (tmpCryptoInfo); + tmpCryptoInfo = NULL; + } + + return STATUS_SUCCESS; + } + } + else if ((mount->bProtectHiddenVolume && volumeType == TC_VOLUME_TYPE_NORMAL) + || mount->nReturnCode != ERR_PASSWORD_WRONG) + { + /* If we are not supposed to protect a hidden volume, the only error that is + tolerated is ERR_PASSWORD_WRONG (to allow mounting a possible hidden volume). + + If we _are_ supposed to protect a hidden volume, we do not tolerate any error + (both volume headers must be successfully decrypted). */ + + break; + } + } + + /* Failed due to some non-OS reason so we drop through and return NT + SUCCESS then nReturnCode is checked later in user-mode */ + + if (mount->nReturnCode == ERR_OUTOFMEMORY) + ntStatus = STATUS_INSUFFICIENT_RESOURCES; + else + ntStatus = STATUS_SUCCESS; + +error: + if (mount->nReturnCode == ERR_SUCCESS) + mount->nReturnCode = ERR_PASSWORD_WRONG; + + if (tmpCryptoInfo != NULL) + { + crypto_close (tmpCryptoInfo); + tmpCryptoInfo = NULL; + } + + if (Extension->cryptoInfo) + { + crypto_close (Extension->cryptoInfo); + Extension->cryptoInfo = NULL; + } + + if (Extension->bTimeStampValid) + { + RestoreTimeStamp (Extension); + } + + /* Close the hDeviceFile */ + if (Extension->hDeviceFile != NULL) + ZwClose (Extension->hDeviceFile); + + /* The cryptoInfo pointer is deallocated if the readheader routines + fail so there is no need to deallocate here */ + + /* Dereference the user-mode file object */ + if (Extension->pfoDeviceFile != NULL) + ObDereferenceObject (Extension->pfoDeviceFile); + + /* Free the tmp IO buffers */ + if (readBuffer != NULL) + TCfree (readBuffer); + + return ntStatus; +} + +void TCCloseVolume (PDEVICE_OBJECT DeviceObject, PEXTENSION Extension) +{ + UNREFERENCED_PARAMETER (DeviceObject); /* Remove compiler warning */ + + if (Extension->hDeviceFile != NULL) + { + if (Extension->bRawDevice == FALSE + && Extension->bTimeStampValid) + { + RestoreTimeStamp (Extension); + } + ZwClose (Extension->hDeviceFile); + } + ObDereferenceObject (Extension->pfoDeviceFile); + if (Extension->cryptoInfo) + { + crypto_close (Extension->cryptoInfo); + Extension->cryptoInfo = NULL; + } +} + + +NTSTATUS TCSendHostDeviceIoControlRequestEx (PDEVICE_OBJECT DeviceObject, + PEXTENSION Extension, + ULONG IoControlCode, + void *InputBuffer, + ULONG InputBufferSize, + void *OutputBuffer, + ULONG OutputBufferSize) +{ + IO_STATUS_BLOCK IoStatusBlock; + NTSTATUS ntStatus; + PIRP Irp; + + UNREFERENCED_PARAMETER(DeviceObject); /* Remove compiler warning */ + + KeClearEvent (&Extension->keVolumeEvent); + + Irp = IoBuildDeviceIoControlRequest (IoControlCode, + Extension->pFsdDevice, + InputBuffer, InputBufferSize, + OutputBuffer, OutputBufferSize, + FALSE, + &Extension->keVolumeEvent, + &IoStatusBlock); + + if (Irp == NULL) + { + Dump ("IRP allocation failed\n"); + return STATUS_INSUFFICIENT_RESOURCES; + } + + // Disk device may be used by filesystem driver which needs file object + IoGetNextIrpStackLocation (Irp) -> FileObject = Extension->pfoDeviceFile; + + ntStatus = IoCallDriver (Extension->pFsdDevice, Irp); + if (ntStatus == STATUS_PENDING) + { + KeWaitForSingleObject (&Extension->keVolumeEvent, Executive, KernelMode, FALSE, NULL); + ntStatus = IoStatusBlock.Status; + } + + return ntStatus; +} + +NTSTATUS TCSendHostDeviceIoControlRequest (PDEVICE_OBJECT DeviceObject, + PEXTENSION Extension, + ULONG IoControlCode, + void *OutputBuffer, + ULONG OutputBufferSize) +{ + return TCSendHostDeviceIoControlRequestEx (DeviceObject, Extension, IoControlCode, NULL, 0, OutputBuffer, OutputBufferSize); +} + +NTSTATUS COMPLETE_IRP (PDEVICE_OBJECT DeviceObject, + PIRP Irp, + NTSTATUS IrpStatus, + ULONG_PTR IrpInformation) +{ + Irp->IoStatus.Status = IrpStatus; + Irp->IoStatus.Information = IrpInformation; + + UNREFERENCED_PARAMETER (DeviceObject); /* Remove compiler warning */ + +#if EXTRA_INFO + if (!NT_SUCCESS (IrpStatus)) + { + PIO_STACK_LOCATION irpSp = IoGetCurrentIrpStackLocation (Irp); + Dump ("COMPLETE_IRP FAILING IRP %ls Flags 0x%08x vpb 0x%08x NTSTATUS 0x%08x\n", TCTranslateCode (irpSp->MajorFunction), + (ULONG) DeviceObject->Flags, (ULONG) DeviceObject->Vpb->Flags, IrpStatus); + } + else + { + PIO_STACK_LOCATION irpSp = IoGetCurrentIrpStackLocation (Irp); + Dump ("COMPLETE_IRP SUCCESS IRP %ls Flags 0x%08x vpb 0x%08x NTSTATUS 0x%08x\n", TCTranslateCode (irpSp->MajorFunction), + (ULONG) DeviceObject->Flags, (ULONG) DeviceObject->Vpb->Flags, IrpStatus); + } +#endif + IoCompleteRequest (Irp, IO_NO_INCREMENT); + return IrpStatus; +} + + +static void RestoreTimeStamp (PEXTENSION Extension) +{ + NTSTATUS ntStatus; + FILE_BASIC_INFORMATION FileBasicInfo; + IO_STATUS_BLOCK IoStatusBlock; + + if (Extension->hDeviceFile != NULL + && Extension->bRawDevice == FALSE + && Extension->bReadOnly == FALSE + && Extension->bTimeStampValid) + { + ntStatus = ZwQueryInformationFile (Extension->hDeviceFile, + &IoStatusBlock, + &FileBasicInfo, + sizeof (FileBasicInfo), + FileBasicInformation); + + if (!NT_SUCCESS (ntStatus)) + { + Dump ("ZwQueryInformationFile failed in RestoreTimeStamp: NTSTATUS 0x%08x\n", + ntStatus); + } + else + { + FileBasicInfo.CreationTime = Extension->fileCreationTime; + FileBasicInfo.LastAccessTime = Extension->fileLastAccessTime; + FileBasicInfo.LastWriteTime = Extension->fileLastWriteTime; + FileBasicInfo.ChangeTime = Extension->fileLastChangeTime; + + ntStatus = ZwSetInformationFile( + Extension->hDeviceFile, + &IoStatusBlock, + &FileBasicInfo, + sizeof (FileBasicInfo), + FileBasicInformation); + + if (!NT_SUCCESS (ntStatus)) + Dump ("ZwSetInformationFile failed in RestoreTimeStamp: NTSTATUS 0x%08x\n",ntStatus); + } + } +} diff --git a/src/Driver/Ntvol.h b/src/Driver/Ntvol.h index 4a496a73..912c02b0 100644 --- a/src/Driver/Ntvol.h +++ b/src/Driver/Ntvol.h @@ -1,22 +1,22 @@ -/* - Legal Notice: Some portions of the source code contained in this file were - derived from the source code of TrueCrypt 7.1a, which is - Copyright (c) 2003-2012 TrueCrypt Developers Association and which is - governed by the TrueCrypt License 3.0, also from the source code of - Encryption for the Masses 2.02a, which is Copyright (c) 1998-2000 Paul Le Roux - and which is governed by the 'License Agreement for Encryption for the Masses' - Modifications and additions to the original source code (contained in this file) - and all other portions of this file are Copyright (c) 2013-2016 IDRIX - and are governed by the Apache License 2.0 the full text of which is - contained in the file License.txt included in VeraCrypt binary and source - code distribution packages. */ - -extern volatile BOOL ProbingHostDeviceForWrite; - -NTSTATUS TCOpenVolume ( PDEVICE_OBJECT DeviceObject , PEXTENSION Extension , MOUNT_STRUCT *mount , PWSTR pwszMountVolume , BOOL bRawDevice ); -void TCCloseVolume ( PDEVICE_OBJECT DeviceObject , PEXTENSION Extension ); -NTSTATUS TCCompletion ( PDEVICE_OBJECT DeviceObject , PIRP Irp , PVOID pUserBuffer ); -static NTSTATUS TCSendHostDeviceIoControlRequest ( PDEVICE_OBJECT DeviceObject , PEXTENSION Extension , ULONG IoControlCode , void *OutputBuffer , ULONG OutputBufferSize ); -static NTSTATUS TCSendHostDeviceIoControlRequestEx ( PDEVICE_OBJECT DeviceObject , PEXTENSION Extension , ULONG IoControlCode , void *InputBuffer , ULONG InputBufferSize , void *OutputBuffer , ULONG OutputBufferSize ); -NTSTATUS COMPLETE_IRP ( PDEVICE_OBJECT DeviceObject , PIRP Irp , NTSTATUS IrpStatus , ULONG_PTR IrpInformation ); -static void RestoreTimeStamp ( PEXTENSION Extension ); +/* + Legal Notice: Some portions of the source code contained in this file were + derived from the source code of TrueCrypt 7.1a, which is + Copyright (c) 2003-2012 TrueCrypt Developers Association and which is + governed by the TrueCrypt License 3.0, also from the source code of + Encryption for the Masses 2.02a, which is Copyright (c) 1998-2000 Paul Le Roux + and which is governed by the 'License Agreement for Encryption for the Masses' + Modifications and additions to the original source code (contained in this file) + and all other portions of this file are Copyright (c) 2013-2016 IDRIX + and are governed by the Apache License 2.0 the full text of which is + contained in the file License.txt included in VeraCrypt binary and source + code distribution packages. */ + +extern volatile BOOL ProbingHostDeviceForWrite; + +NTSTATUS TCOpenVolume ( PDEVICE_OBJECT DeviceObject , PEXTENSION Extension , MOUNT_STRUCT *mount , PWSTR pwszMountVolume , BOOL bRawDevice ); +void TCCloseVolume ( PDEVICE_OBJECT DeviceObject , PEXTENSION Extension ); +NTSTATUS TCCompletion ( PDEVICE_OBJECT DeviceObject , PIRP Irp , PVOID pUserBuffer ); +static NTSTATUS TCSendHostDeviceIoControlRequest ( PDEVICE_OBJECT DeviceObject , PEXTENSION Extension , ULONG IoControlCode , void *OutputBuffer , ULONG OutputBufferSize ); +static NTSTATUS TCSendHostDeviceIoControlRequestEx ( PDEVICE_OBJECT DeviceObject , PEXTENSION Extension , ULONG IoControlCode , void *InputBuffer , ULONG InputBufferSize , void *OutputBuffer , ULONG OutputBufferSize ); +NTSTATUS COMPLETE_IRP ( PDEVICE_OBJECT DeviceObject , PIRP Irp , NTSTATUS IrpStatus , ULONG_PTR IrpInformation ); +static void RestoreTimeStamp ( PEXTENSION Extension ); diff --git a/src/Driver/Resource.h b/src/Driver/Resource.h index effd8044..a3e73646 100644 --- a/src/Driver/Resource.h +++ b/src/Driver/Resource.h @@ -1,16 +1,16 @@ -//{{NO_DEPENDENCIES}} -// Microsoft Visual C++ generated include file. -// Used by Driver.rc -// - -// Next default values for new objects -// -#ifdef APSTUDIO_INVOKED -#ifndef APSTUDIO_READONLY_SYMBOLS -#define _APS_NO_MFC 1 -#define _APS_NEXT_RESOURCE_VALUE 101 -#define _APS_NEXT_COMMAND_VALUE 40001 -#define _APS_NEXT_CONTROL_VALUE 1001 -#define _APS_NEXT_SYMED_VALUE 101 -#endif -#endif +//{{NO_DEPENDENCIES}} +// Microsoft Visual C++ generated include file. +// Used by Driver.rc +// + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NO_MFC 1 +#define _APS_NEXT_RESOURCE_VALUE 101 +#define _APS_NEXT_COMMAND_VALUE 40001 +#define _APS_NEXT_CONTROL_VALUE 1001 +#define _APS_NEXT_SYMED_VALUE 101 +#endif +#endif diff --git a/src/Driver/Sources b/src/Driver/Sources index 592c2dc6..8e8aef2d 100644 --- a/src/Driver/Sources +++ b/src/Driver/Sources @@ -1,21 +1,21 @@ -TARGETNAME=veracrypt -TARGETTYPE=DRIVER - -USER_C_FLAGS=$(USER_C_FLAGS) -D_UNICODE -LINKER_FLAGS=$(LINKER_FLAGS) -map - -INCLUDES = ../Common;../Crypto - -SOURCES = \ - DriveFilter.c \ - DumpFilter.c \ - EncryptedIoQueue.c \ - Ntdriver.c \ - Ntvol.c \ - VolumeFilter.c \ - Driver.rc - -TARGETLIBS = \ - $(SDK_LIB_PATH)/uuid.lib \ - ../Common/obj$(BUILD_ALT_DIR)/*/Common.lib \ - ../Crypto/obj$(BUILD_ALT_DIR)/*/Crypto.lib +TARGETNAME=veracrypt +TARGETTYPE=DRIVER + +USER_C_FLAGS=$(USER_C_FLAGS) -D_UNICODE +LINKER_FLAGS=$(LINKER_FLAGS) -map + +INCLUDES = ../Common;../Crypto + +SOURCES = \ + DriveFilter.c \ + DumpFilter.c \ + EncryptedIoQueue.c \ + Ntdriver.c \ + Ntvol.c \ + VolumeFilter.c \ + Driver.rc + +TARGETLIBS = \ + $(SDK_LIB_PATH)/uuid.lib \ + ../Common/obj$(BUILD_ALT_DIR)/*/Common.lib \ + ../Crypto/obj$(BUILD_ALT_DIR)/*/Crypto.lib diff --git a/src/Driver/VolumeFilter.c b/src/Driver/VolumeFilter.c index 78b2d7b9..8cb675f6 100644 --- a/src/Driver/VolumeFilter.c +++ b/src/Driver/VolumeFilter.c @@ -1,299 +1,299 @@ -/* - Derived from source code of TrueCrypt 7.1a, which is - Copyright (c) 2008-2012 TrueCrypt Developers Association and which is governed - by the TrueCrypt License 3.0. - - Modifications and additions to the original source code (contained in this file) - and all other portions of this file are Copyright (c) 2013-2016 IDRIX - and are governed by the Apache License 2.0 the full text of which is - contained in the file License.txt included in VeraCrypt binary and source - code distribution packages. -*/ - -#include "TCdefs.h" -#include "Ntdriver.h" -#include "Ntvol.h" -#include "DriveFilter.h" -#include "VolumeFilter.h" - -typedef DriveFilterExtension VolumeFilterExtension; - -// Number of times the filter driver answered that an unencrypted volume -// is read-only (or mounted an outer/normal TrueCrypt volume as read only) -uint32 HiddenSysLeakProtectionCount = 0; - - -NTSTATUS VolumeFilterAddDevice (PDRIVER_OBJECT driverObject, PDEVICE_OBJECT pdo) -{ - VolumeFilterExtension *Extension; - NTSTATUS status; - PDEVICE_OBJECT filterDeviceObject = NULL; - PDEVICE_OBJECT attachedDeviceObject; - - Dump ("VolumeFilterAddDevice pdo=%p\n", pdo); - - attachedDeviceObject = IoGetAttachedDeviceReference (pdo); - status = IoCreateDevice (driverObject, sizeof (VolumeFilterExtension), NULL, attachedDeviceObject->DeviceType, 0, FALSE, &filterDeviceObject); - - ObDereferenceObject (attachedDeviceObject); - - if (!NT_SUCCESS (status)) - { - filterDeviceObject = NULL; - goto err; - } - - Extension = (VolumeFilterExtension *) filterDeviceObject->DeviceExtension; - memset (Extension, 0, sizeof (VolumeFilterExtension)); - - status = IoAttachDeviceToDeviceStackSafe (filterDeviceObject, pdo, &(Extension->LowerDeviceObject)); - if (status != STATUS_SUCCESS) - { - goto err; - } - - if (!Extension->LowerDeviceObject) - { - status = STATUS_DEVICE_REMOVED; - goto err; - } - - Extension->IsVolumeFilterDevice = TRUE; - Extension->DeviceObject = filterDeviceObject; - Extension->Pdo = pdo; - - IoInitializeRemoveLock (&Extension->Queue.RemoveLock, 'LRCV', 0, 0); - - filterDeviceObject->Flags |= Extension->LowerDeviceObject->Flags & (DO_DIRECT_IO | DO_BUFFERED_IO | DO_POWER_PAGABLE); - filterDeviceObject->Flags &= ~DO_DEVICE_INITIALIZING; - - return status; - -err: - if (filterDeviceObject) - { - if (Extension->LowerDeviceObject) - IoDetachDevice (Extension->LowerDeviceObject); - - IoDeleteDevice (filterDeviceObject); - } - - return status; -} - - -static NTSTATUS PassIrp (PDEVICE_OBJECT deviceObject, PIRP irp) -{ - IoSkipCurrentIrpStackLocation (irp); - return IoCallDriver (deviceObject, irp); -} - - -static NTSTATUS PassFilteredIrp (PDEVICE_OBJECT deviceObject, PIRP irp, PIO_COMPLETION_ROUTINE completionRoutine, PVOID completionRoutineArg) -{ - IoCopyCurrentIrpStackLocationToNext (irp); - - if (completionRoutine) - IoSetCompletionRoutine (irp, completionRoutine, completionRoutineArg, TRUE, TRUE, TRUE); - - return IoCallDriver (deviceObject, irp); -} - - -static NTSTATUS OnDeviceUsageNotificationCompleted (PDEVICE_OBJECT filterDeviceObject, PIRP Irp, VolumeFilterExtension *Extension) -{ - if (Irp->PendingReturned) - IoMarkIrpPending (Irp); - - if (!(Extension->LowerDeviceObject->Flags & DO_POWER_PAGABLE)) - filterDeviceObject->Flags &= ~DO_POWER_PAGABLE; - - IoReleaseRemoveLock (&Extension->Queue.RemoveLock, Irp); - return STATUS_CONTINUE_COMPLETION; -} - - -static NTSTATUS OnStartDeviceCompleted (PDEVICE_OBJECT filterDeviceObject, PIRP Irp, VolumeFilterExtension *Extension) -{ - if (Irp->PendingReturned) - IoMarkIrpPending (Irp); - - if (Extension->LowerDeviceObject->Characteristics & FILE_REMOVABLE_MEDIA) - filterDeviceObject->Characteristics |= FILE_REMOVABLE_MEDIA; - - IoReleaseRemoveLock (&Extension->Queue.RemoveLock, Irp); - return STATUS_CONTINUE_COMPLETION; -} - - -static NTSTATUS DispatchControl (PDEVICE_OBJECT DeviceObject, PIRP Irp, VolumeFilterExtension *Extension, PIO_STACK_LOCATION irpSp) -{ - NTSTATUS status = IoAcquireRemoveLock (&Extension->Queue.RemoveLock, Irp); - if (!NT_SUCCESS (status)) - return TCCompleteIrp (Irp, status, 0); - - if (IsHiddenSystemRunning()) - { - switch (irpSp->Parameters.DeviceIoControl.IoControlCode) - { - case IOCTL_DISK_IS_WRITABLE: - { - // All volumes except the system volume must be read-only - - DriveFilterExtension *bootDriveExtension = GetBootDriveFilterExtension(); - STORAGE_DEVICE_NUMBER storageDeviceNumber; - - if (!bootDriveExtension->SystemStorageDeviceNumberValid) - TC_BUG_CHECK (STATUS_INVALID_PARAMETER); - - status = SendDeviceIoControlRequest (Extension->LowerDeviceObject, IOCTL_STORAGE_GET_DEVICE_NUMBER, NULL, 0, &storageDeviceNumber, sizeof (storageDeviceNumber)); - - if (NT_SUCCESS (status) && bootDriveExtension->SystemStorageDeviceNumber == storageDeviceNumber.DeviceNumber) - { - PARTITION_INFORMATION_EX partition; - status = SendDeviceIoControlRequest (Extension->LowerDeviceObject, IOCTL_DISK_GET_PARTITION_INFO_EX, NULL, 0, &partition, sizeof (partition)); - - if (NT_SUCCESS (status) && partition.StartingOffset.QuadPart == bootDriveExtension->ConfiguredEncryptedAreaStart) - { - IoReleaseRemoveLock (&Extension->Queue.RemoveLock, Irp); - return TCCompleteDiskIrp (Irp, STATUS_SUCCESS, 0); - } - } - - IoReleaseRemoveLock (&Extension->Queue.RemoveLock, Irp); - - ++HiddenSysLeakProtectionCount; - return TCCompleteDiskIrp (Irp, STATUS_MEDIA_WRITE_PROTECTED, 0); - } - - case TC_IOCTL_DISK_IS_WRITABLE: - Dump ("TC_IOCTL_DISK_IS_WRITABLE pdo=%p\n", Extension->Pdo); - - if (!ProbingHostDeviceForWrite) - break; - - // Probe the real state of the device as the user is mounting a TrueCrypt volume. - - // Volume filter may be attached to a merged drive+volume PDO. First test if TC_IOCTL_DISK_IS_WRITABLE works for the underlying device. - status = SendDeviceIoControlRequest (Extension->LowerDeviceObject, TC_IOCTL_DISK_IS_WRITABLE, NULL, 0, NULL, 0); - - if (NT_SUCCESS (status) || status == STATUS_MEDIA_WRITE_PROTECTED) - { - IoReleaseRemoveLock (&Extension->Queue.RemoveLock, Irp); - return TCCompleteDiskIrp (Irp, status, 0); - } - - status = SendDeviceIoControlRequest (Extension->LowerDeviceObject, IOCTL_DISK_IS_WRITABLE, NULL, 0, NULL, 0); - - IoReleaseRemoveLock (&Extension->Queue.RemoveLock, Irp); - return TCCompleteDiskIrp (Irp, status, 0); - - case IOCTL_STORAGE_MANAGE_DATA_SET_ATTRIBUTES: - - // Filter IOCTL_STORAGE_MANAGE_DATA_SET_ATTRIBUTES to enable potential future use of hidden systems on drives that use the trim operation but not wear-leveling (if any appear in future). The documentation forbids users to create hidden volumes/systems on drives that use wear-leveling and consequently also on drives that use trim (as trim is used only by drives that use wear-leveling, as of 2010). - - IoReleaseRemoveLock (&Extension->Queue.RemoveLock, Irp); - return TCCompleteDiskIrp (Irp, STATUS_SUCCESS, 0); - } - } - - status = PassIrp (Extension->LowerDeviceObject, Irp); - IoReleaseRemoveLock (&Extension->Queue.RemoveLock, Irp); - return status; -} - - -static NTSTATUS DispatchPnp (PDEVICE_OBJECT DeviceObject, PIRP Irp, VolumeFilterExtension *Extension, PIO_STACK_LOCATION irpSp) -{ - NTSTATUS status; - - status = IoAcquireRemoveLock (&Extension->Queue.RemoveLock, Irp); - if (!NT_SUCCESS (status)) - return TCCompleteIrp (Irp, status, 0); - - switch (irpSp->MinorFunction) - { - case IRP_MN_START_DEVICE: - Dump ("IRP_MN_START_DEVICE volume pdo=%p\n", Extension->Pdo); - return PassFilteredIrp (Extension->LowerDeviceObject, Irp, OnStartDeviceCompleted, Extension); - - case IRP_MN_DEVICE_USAGE_NOTIFICATION: - { - PDEVICE_OBJECT attachedDevice = IoGetAttachedDeviceReference (DeviceObject); - - if (attachedDevice == DeviceObject || (attachedDevice->Flags & DO_POWER_PAGABLE)) - DeviceObject->Flags |= DO_POWER_PAGABLE; - - ObDereferenceObject (attachedDevice); - } - - return PassFilteredIrp (Extension->LowerDeviceObject, Irp, OnDeviceUsageNotificationCompleted, Extension); - - - case IRP_MN_REMOVE_DEVICE: - Dump ("IRP_MN_REMOVE_DEVICE volume pdo=%p\n", Extension->Pdo); - - IoReleaseRemoveLockAndWait (&Extension->Queue.RemoveLock, Irp); - status = PassIrp (Extension->LowerDeviceObject, Irp); - - IoDetachDevice (Extension->LowerDeviceObject); - - IoDeleteDevice (DeviceObject); - return status; - - default: - status = PassIrp (Extension->LowerDeviceObject, Irp); - IoReleaseRemoveLock (&Extension->Queue.RemoveLock, Irp); - } - - return status; -} - - -static NTSTATUS DispatchPower (PDEVICE_OBJECT DeviceObject, PIRP Irp, VolumeFilterExtension *Extension, PIO_STACK_LOCATION irpSp) -{ - NTSTATUS status; - PoStartNextPowerIrp (Irp); - - status = IoAcquireRemoveLock (&Extension->Queue.RemoveLock, Irp); - if (!NT_SUCCESS (status)) - return TCCompleteIrp (Irp, status, 0); - - IoSkipCurrentIrpStackLocation (Irp); - status = PoCallDriver (Extension->LowerDeviceObject, Irp); - - IoReleaseRemoveLock (&Extension->Queue.RemoveLock, Irp); - return status; -} - - -NTSTATUS VolumeFilterDispatchIrp (PDEVICE_OBJECT DeviceObject, PIRP Irp) -{ - VolumeFilterExtension *Extension = (VolumeFilterExtension *) DeviceObject->DeviceExtension; - PIO_STACK_LOCATION irpSp = IoGetCurrentIrpStackLocation (Irp); - NTSTATUS status; - - ASSERT (!Extension->bRootDevice && Extension->IsVolumeFilterDevice); - - switch (irpSp->MajorFunction) - { - case IRP_MJ_DEVICE_CONTROL: - return DispatchControl (DeviceObject, Irp, Extension, irpSp); - - case IRP_MJ_PNP: - return DispatchPnp (DeviceObject, Irp, Extension, irpSp); - - case IRP_MJ_POWER: - return DispatchPower (DeviceObject, Irp, Extension, irpSp); - - default: - status = IoAcquireRemoveLock (&Extension->Queue.RemoveLock, Irp); - if (!NT_SUCCESS (status)) - return TCCompleteIrp (Irp, status, 0); - - status = PassIrp (Extension->LowerDeviceObject, Irp); - - IoReleaseRemoveLock (&Extension->Queue.RemoveLock, Irp); - return status; - } -} +/* + Derived from source code of TrueCrypt 7.1a, which is + Copyright (c) 2008-2012 TrueCrypt Developers Association and which is governed + by the TrueCrypt License 3.0. + + Modifications and additions to the original source code (contained in this file) + and all other portions of this file are Copyright (c) 2013-2016 IDRIX + and are governed by the Apache License 2.0 the full text of which is + contained in the file License.txt included in VeraCrypt binary and source + code distribution packages. +*/ + +#include "TCdefs.h" +#include "Ntdriver.h" +#include "Ntvol.h" +#include "DriveFilter.h" +#include "VolumeFilter.h" + +typedef DriveFilterExtension VolumeFilterExtension; + +// Number of times the filter driver answered that an unencrypted volume +// is read-only (or mounted an outer/normal TrueCrypt volume as read only) +uint32 HiddenSysLeakProtectionCount = 0; + + +NTSTATUS VolumeFilterAddDevice (PDRIVER_OBJECT driverObject, PDEVICE_OBJECT pdo) +{ + VolumeFilterExtension *Extension; + NTSTATUS status; + PDEVICE_OBJECT filterDeviceObject = NULL; + PDEVICE_OBJECT attachedDeviceObject; + + Dump ("VolumeFilterAddDevice pdo=%p\n", pdo); + + attachedDeviceObject = IoGetAttachedDeviceReference (pdo); + status = IoCreateDevice (driverObject, sizeof (VolumeFilterExtension), NULL, attachedDeviceObject->DeviceType, 0, FALSE, &filterDeviceObject); + + ObDereferenceObject (attachedDeviceObject); + + if (!NT_SUCCESS (status)) + { + filterDeviceObject = NULL; + goto err; + } + + Extension = (VolumeFilterExtension *) filterDeviceObject->DeviceExtension; + memset (Extension, 0, sizeof (VolumeFilterExtension)); + + status = IoAttachDeviceToDeviceStackSafe (filterDeviceObject, pdo, &(Extension->LowerDeviceObject)); + if (status != STATUS_SUCCESS) + { + goto err; + } + + if (!Extension->LowerDeviceObject) + { + status = STATUS_DEVICE_REMOVED; + goto err; + } + + Extension->IsVolumeFilterDevice = TRUE; + Extension->DeviceObject = filterDeviceObject; + Extension->Pdo = pdo; + + IoInitializeRemoveLock (&Extension->Queue.RemoveLock, 'LRCV', 0, 0); + + filterDeviceObject->Flags |= Extension->LowerDeviceObject->Flags & (DO_DIRECT_IO | DO_BUFFERED_IO | DO_POWER_PAGABLE); + filterDeviceObject->Flags &= ~DO_DEVICE_INITIALIZING; + + return status; + +err: + if (filterDeviceObject) + { + if (Extension->LowerDeviceObject) + IoDetachDevice (Extension->LowerDeviceObject); + + IoDeleteDevice (filterDeviceObject); + } + + return status; +} + + +static NTSTATUS PassIrp (PDEVICE_OBJECT deviceObject, PIRP irp) +{ + IoSkipCurrentIrpStackLocation (irp); + return IoCallDriver (deviceObject, irp); +} + + +static NTSTATUS PassFilteredIrp (PDEVICE_OBJECT deviceObject, PIRP irp, PIO_COMPLETION_ROUTINE completionRoutine, PVOID completionRoutineArg) +{ + IoCopyCurrentIrpStackLocationToNext (irp); + + if (completionRoutine) + IoSetCompletionRoutine (irp, completionRoutine, completionRoutineArg, TRUE, TRUE, TRUE); + + return IoCallDriver (deviceObject, irp); +} + + +static NTSTATUS OnDeviceUsageNotificationCompleted (PDEVICE_OBJECT filterDeviceObject, PIRP Irp, VolumeFilterExtension *Extension) +{ + if (Irp->PendingReturned) + IoMarkIrpPending (Irp); + + if (!(Extension->LowerDeviceObject->Flags & DO_POWER_PAGABLE)) + filterDeviceObject->Flags &= ~DO_POWER_PAGABLE; + + IoReleaseRemoveLock (&Extension->Queue.RemoveLock, Irp); + return STATUS_CONTINUE_COMPLETION; +} + + +static NTSTATUS OnStartDeviceCompleted (PDEVICE_OBJECT filterDeviceObject, PIRP Irp, VolumeFilterExtension *Extension) +{ + if (Irp->PendingReturned) + IoMarkIrpPending (Irp); + + if (Extension->LowerDeviceObject->Characteristics & FILE_REMOVABLE_MEDIA) + filterDeviceObject->Characteristics |= FILE_REMOVABLE_MEDIA; + + IoReleaseRemoveLock (&Extension->Queue.RemoveLock, Irp); + return STATUS_CONTINUE_COMPLETION; +} + + +static NTSTATUS DispatchControl (PDEVICE_OBJECT DeviceObject, PIRP Irp, VolumeFilterExtension *Extension, PIO_STACK_LOCATION irpSp) +{ + NTSTATUS status = IoAcquireRemoveLock (&Extension->Queue.RemoveLock, Irp); + if (!NT_SUCCESS (status)) + return TCCompleteIrp (Irp, status, 0); + + if (IsHiddenSystemRunning()) + { + switch (irpSp->Parameters.DeviceIoControl.IoControlCode) + { + case IOCTL_DISK_IS_WRITABLE: + { + // All volumes except the system volume must be read-only + + DriveFilterExtension *bootDriveExtension = GetBootDriveFilterExtension(); + STORAGE_DEVICE_NUMBER storageDeviceNumber; + + if (!bootDriveExtension->SystemStorageDeviceNumberValid) + TC_BUG_CHECK (STATUS_INVALID_PARAMETER); + + status = SendDeviceIoControlRequest (Extension->LowerDeviceObject, IOCTL_STORAGE_GET_DEVICE_NUMBER, NULL, 0, &storageDeviceNumber, sizeof (storageDeviceNumber)); + + if (NT_SUCCESS (status) && bootDriveExtension->SystemStorageDeviceNumber == storageDeviceNumber.DeviceNumber) + { + PARTITION_INFORMATION_EX partition; + status = SendDeviceIoControlRequest (Extension->LowerDeviceObject, IOCTL_DISK_GET_PARTITION_INFO_EX, NULL, 0, &partition, sizeof (partition)); + + if (NT_SUCCESS (status) && partition.StartingOffset.QuadPart == bootDriveExtension->ConfiguredEncryptedAreaStart) + { + IoReleaseRemoveLock (&Extension->Queue.RemoveLock, Irp); + return TCCompleteDiskIrp (Irp, STATUS_SUCCESS, 0); + } + } + + IoReleaseRemoveLock (&Extension->Queue.RemoveLock, Irp); + + ++HiddenSysLeakProtectionCount; + return TCCompleteDiskIrp (Irp, STATUS_MEDIA_WRITE_PROTECTED, 0); + } + + case TC_IOCTL_DISK_IS_WRITABLE: + Dump ("TC_IOCTL_DISK_IS_WRITABLE pdo=%p\n", Extension->Pdo); + + if (!ProbingHostDeviceForWrite) + break; + + // Probe the real state of the device as the user is mounting a TrueCrypt volume. + + // Volume filter may be attached to a merged drive+volume PDO. First test if TC_IOCTL_DISK_IS_WRITABLE works for the underlying device. + status = SendDeviceIoControlRequest (Extension->LowerDeviceObject, TC_IOCTL_DISK_IS_WRITABLE, NULL, 0, NULL, 0); + + if (NT_SUCCESS (status) || status == STATUS_MEDIA_WRITE_PROTECTED) + { + IoReleaseRemoveLock (&Extension->Queue.RemoveLock, Irp); + return TCCompleteDiskIrp (Irp, status, 0); + } + + status = SendDeviceIoControlRequest (Extension->LowerDeviceObject, IOCTL_DISK_IS_WRITABLE, NULL, 0, NULL, 0); + + IoReleaseRemoveLock (&Extension->Queue.RemoveLock, Irp); + return TCCompleteDiskIrp (Irp, status, 0); + + case IOCTL_STORAGE_MANAGE_DATA_SET_ATTRIBUTES: + + // Filter IOCTL_STORAGE_MANAGE_DATA_SET_ATTRIBUTES to enable potential future use of hidden systems on drives that use the trim operation but not wear-leveling (if any appear in future). The documentation forbids users to create hidden volumes/systems on drives that use wear-leveling and consequently also on drives that use trim (as trim is used only by drives that use wear-leveling, as of 2010). + + IoReleaseRemoveLock (&Extension->Queue.RemoveLock, Irp); + return TCCompleteDiskIrp (Irp, STATUS_SUCCESS, 0); + } + } + + status = PassIrp (Extension->LowerDeviceObject, Irp); + IoReleaseRemoveLock (&Extension->Queue.RemoveLock, Irp); + return status; +} + + +static NTSTATUS DispatchPnp (PDEVICE_OBJECT DeviceObject, PIRP Irp, VolumeFilterExtension *Extension, PIO_STACK_LOCATION irpSp) +{ + NTSTATUS status; + + status = IoAcquireRemoveLock (&Extension->Queue.RemoveLock, Irp); + if (!NT_SUCCESS (status)) + return TCCompleteIrp (Irp, status, 0); + + switch (irpSp->MinorFunction) + { + case IRP_MN_START_DEVICE: + Dump ("IRP_MN_START_DEVICE volume pdo=%p\n", Extension->Pdo); + return PassFilteredIrp (Extension->LowerDeviceObject, Irp, OnStartDeviceCompleted, Extension); + + case IRP_MN_DEVICE_USAGE_NOTIFICATION: + { + PDEVICE_OBJECT attachedDevice = IoGetAttachedDeviceReference (DeviceObject); + + if (attachedDevice == DeviceObject || (attachedDevice->Flags & DO_POWER_PAGABLE)) + DeviceObject->Flags |= DO_POWER_PAGABLE; + + ObDereferenceObject (attachedDevice); + } + + return PassFilteredIrp (Extension->LowerDeviceObject, Irp, OnDeviceUsageNotificationCompleted, Extension); + + + case IRP_MN_REMOVE_DEVICE: + Dump ("IRP_MN_REMOVE_DEVICE volume pdo=%p\n", Extension->Pdo); + + IoReleaseRemoveLockAndWait (&Extension->Queue.RemoveLock, Irp); + status = PassIrp (Extension->LowerDeviceObject, Irp); + + IoDetachDevice (Extension->LowerDeviceObject); + + IoDeleteDevice (DeviceObject); + return status; + + default: + status = PassIrp (Extension->LowerDeviceObject, Irp); + IoReleaseRemoveLock (&Extension->Queue.RemoveLock, Irp); + } + + return status; +} + + +static NTSTATUS DispatchPower (PDEVICE_OBJECT DeviceObject, PIRP Irp, VolumeFilterExtension *Extension, PIO_STACK_LOCATION irpSp) +{ + NTSTATUS status; + PoStartNextPowerIrp (Irp); + + status = IoAcquireRemoveLock (&Extension->Queue.RemoveLock, Irp); + if (!NT_SUCCESS (status)) + return TCCompleteIrp (Irp, status, 0); + + IoSkipCurrentIrpStackLocation (Irp); + status = PoCallDriver (Extension->LowerDeviceObject, Irp); + + IoReleaseRemoveLock (&Extension->Queue.RemoveLock, Irp); + return status; +} + + +NTSTATUS VolumeFilterDispatchIrp (PDEVICE_OBJECT DeviceObject, PIRP Irp) +{ + VolumeFilterExtension *Extension = (VolumeFilterExtension *) DeviceObject->DeviceExtension; + PIO_STACK_LOCATION irpSp = IoGetCurrentIrpStackLocation (Irp); + NTSTATUS status; + + ASSERT (!Extension->bRootDevice && Extension->IsVolumeFilterDevice); + + switch (irpSp->MajorFunction) + { + case IRP_MJ_DEVICE_CONTROL: + return DispatchControl (DeviceObject, Irp, Extension, irpSp); + + case IRP_MJ_PNP: + return DispatchPnp (DeviceObject, Irp, Extension, irpSp); + + case IRP_MJ_POWER: + return DispatchPower (DeviceObject, Irp, Extension, irpSp); + + default: + status = IoAcquireRemoveLock (&Extension->Queue.RemoveLock, Irp); + if (!NT_SUCCESS (status)) + return TCCompleteIrp (Irp, status, 0); + + status = PassIrp (Extension->LowerDeviceObject, Irp); + + IoReleaseRemoveLock (&Extension->Queue.RemoveLock, Irp); + return status; + } +} diff --git a/src/Driver/VolumeFilter.h b/src/Driver/VolumeFilter.h index ace7627a..be0cbd0b 100644 --- a/src/Driver/VolumeFilter.h +++ b/src/Driver/VolumeFilter.h @@ -1,23 +1,23 @@ -/* - Derived from source code of TrueCrypt 7.1a, which is - Copyright (c) 2008-2012 TrueCrypt Developers Association and which is governed - by the TrueCrypt License 3.0. - - Modifications and additions to the original source code (contained in this file) - and all other portions of this file are Copyright (c) 2013-2016 IDRIX - and are governed by the Apache License 2.0 the full text of which is - contained in the file License.txt included in VeraCrypt binary and source - code distribution packages. -*/ - -#ifndef TC_HEADER_DRIVER_VOLUME_FILTER -#define TC_HEADER_DRIVER_VOLUME_FILTER - -#include "TCdefs.h" - -extern uint32 HiddenSysLeakProtectionCount; - -NTSTATUS VolumeFilterAddDevice (PDRIVER_OBJECT driverObject, PDEVICE_OBJECT pdo); -NTSTATUS VolumeFilterDispatchIrp (PDEVICE_OBJECT DeviceObject, PIRP Irp); - -#endif // TC_HEADER_DRIVER_VOLUME_FILTER +/* + Derived from source code of TrueCrypt 7.1a, which is + Copyright (c) 2008-2012 TrueCrypt Developers Association and which is governed + by the TrueCrypt License 3.0. + + Modifications and additions to the original source code (contained in this file) + and all other portions of this file are Copyright (c) 2013-2016 IDRIX + and are governed by the Apache License 2.0 the full text of which is + contained in the file License.txt included in VeraCrypt binary and source + code distribution packages. +*/ + +#ifndef TC_HEADER_DRIVER_VOLUME_FILTER +#define TC_HEADER_DRIVER_VOLUME_FILTER + +#include "TCdefs.h" + +extern uint32 HiddenSysLeakProtectionCount; + +NTSTATUS VolumeFilterAddDevice (PDRIVER_OBJECT driverObject, PDEVICE_OBJECT pdo); +NTSTATUS VolumeFilterDispatchIrp (PDEVICE_OBJECT DeviceObject, PIRP Irp); + +#endif // TC_HEADER_DRIVER_VOLUME_FILTER -- cgit v1.2.3