From c606f0866c3a2a5db3ef9bc41738ef33eb9612a9 Mon Sep 17 00:00:00 2001 From: Mounir IDRASSI Date: Sat, 22 Jun 2013 16:16:13 +0200 Subject: Add original TrueCrypt 7.1a sources --- src/Driver/DriveFilter.c | 1942 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 1942 insertions(+) create mode 100644 src/Driver/DriveFilter.c (limited to 'src/Driver/DriveFilter.c') diff --git a/src/Driver/DriveFilter.c b/src/Driver/DriveFilter.c new file mode 100644 index 00000000..72adb86f --- /dev/null +++ b/src/Driver/DriveFilter.c @@ -0,0 +1,1942 @@ +/* + Copyright (c) 2008-2012 TrueCrypt Developers Association. All rights reserved. + + Governed by the TrueCrypt License 3.0 the full text of which is contained in + the file License.txt included in TrueCrypt binary and source code distribution + packages. +*/ + +#include "TCdefs.h" +#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 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); + } + + BootLoaderSegment = bootLoaderSegment; + + BootArgs = *bootArguments; + BootArgsValid = TRUE; + memset (bootArguments, 0, sizeof (*bootArguments)); + + if (BootArgs.BootLoaderVersion < 0x600) + { + BootArgs.HiddenSystemPartitionStart = 0; + BootArgs.DecoySystemPartitionStart = 0; + } + + if (BootArgs.BootLoaderVersion < 0x630) + BootArgs.Flags = 0; + + BootDriveSignatureValid = (BootArgs.BootLoaderVersion >= 0x710); + + 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) + AddPasswordToCache (&BootArgs.BootPassword); + + 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)); + + Extension->LowerDeviceObject = IoAttachDeviceToDeviceStack (filterDeviceObject, pdo); // IoAttachDeviceToDeviceStackSafe() is not required in AddDevice routine and is also unavailable on Windows 2000 SP4 + 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, 'LRCT', 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 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; + + 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 (ReadVolumeHeader (!hiddenVolume, header, password, &Extension->Queue.CryptoInfo, Extension->HeaderCryptoInfo) == 0) + { + // Header decrypted + status = STATUS_SUCCESS; + Dump ("Header decrypted\n"); + + 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 (BootArgs.CryptoInfoLength > 0) + { + PHYSICAL_ADDRESS cryptoInfoAddress; + byte *mappedCryptoInfo; + + cryptoInfoAddress.QuadPart = (BootLoaderSegment << 4) + BootArgs.CryptoInfoOffset; + mappedCryptoInfo = MmMapIoSpace (cryptoInfoAddress, BootArgs.CryptoInfoLength, MmCached); + + if (mappedCryptoInfo) + { + Dump ("Wiping memory %x %d\n", cryptoInfoAddress.LowPart, BootArgs.CryptoInfoLength); + memset (mappedCryptoInfo, 0, BootArgs.CryptoInfoLength); + MmUnmapIoSpace (mappedCryptoInfo, BootArgs.CryptoInfoLength); + } + } + + 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) != 0x54525545) + { + 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) + { + 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, NULL, BootDriveFilterExtension->HeaderCryptoInfo) == 0) + { + Dump ("Header reopened\n"); + + 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; + + 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; + + 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; + for (wipePass = 1; wipePass <= GetWipePassCount (SetupRequest.WipeAlgorithm); ++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)); + + 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) + TCfree (buffer); + if (wipeBuffer) + TCfree (wipeBuffer); + + 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; +#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 GetBootEncryptionAlgorithmName (PIRP irp, PIO_STACK_LOCATION irpSp) +{ + if (ValidateIOBufferSize (irp, sizeof (GetBootEncryptionAlgorithmNameRequest), ValidateOutput)) + { + if (BootDriveFilterExtension && BootDriveFilterExtension->DriveMounted) + { + GetBootEncryptionAlgorithmNameRequest *request = (GetBootEncryptionAlgorithmNameRequest *) irp->AssociatedIrp.SystemBuffer; + EAGetName (request->BootEncryptionAlgorithmName, BootDriveFilterExtension->Queue.CryptoInfo->ea); + + 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; + 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; + + for (wipePass = 1; wipePass <= GetWipePassCount (WipeDecoyRequest.WipeAlgorithm); ++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)); +} -- cgit v1.2.3