mirror of
https://github.com/veracrypt/VeraCrypt.git
synced 2026-06-21 03:55:02 -05:00
Windows: allow cancelling long mount operations
Add a root-driver abort IOCTL that bypasses the mount control mutex and sets cooperative KDF abort flags for the active mount. Restrict abort requests to privileged callers or to the user that initiated the pending mount, and retry early wait-dialog cancel requests until the driver has registered the cancellable mount context. Wire the wait dialog Cancel button to send the abort request through a fresh driver handle, and propagate ERR_USER_ABORT through header/cache processing. Add a /cancelmount command-line switch that sends the same abort request without displaying UI, so users can cancel hidden-wait-dialog mount operations from another process.
This commit is contained in:
@@ -122,6 +122,8 @@
|
||||
PDRIVER_OBJECT TCDriverObject;
|
||||
PDEVICE_OBJECT RootDeviceObject = NULL;
|
||||
static KMUTEX RootDeviceControlMutex;
|
||||
static KMUTEX MountCancelContextMutex;
|
||||
static MOUNT_CANCEL_CONTEXT ActiveMountCancelContext;
|
||||
BOOL DriverShuttingDown = FALSE;
|
||||
BOOL SelfTestsPassed;
|
||||
int LastUniqueVolumeId;
|
||||
@@ -570,6 +572,9 @@ NTSTATUS TCDispatchQueueIRP (PDEVICE_OBJECT DeviceObject, PIRP Irp)
|
||||
{
|
||||
if (irpSp->MajorFunction == IRP_MJ_DEVICE_CONTROL)
|
||||
{
|
||||
if (irpSp->Parameters.DeviceIoControl.IoControlCode == TC_IOCTL_ABORT_MOUNT_VOLUME)
|
||||
return ProcessMainDeviceControlIrp (DeviceObject, Extension, Irp);
|
||||
|
||||
NTSTATUS status = KeWaitForMutexObject (&RootDeviceControlMutex, Executive, KernelMode, FALSE, NULL);
|
||||
if (!NT_SUCCESS (status))
|
||||
return status;
|
||||
@@ -694,6 +699,7 @@ NTSTATUS TCCreateRootDeviceObject (PDRIVER_OBJECT DriverObject)
|
||||
*bRootExtension = TRUE;
|
||||
|
||||
KeInitializeMutex (&RootDeviceControlMutex, 0);
|
||||
KeInitializeMutex (&MountCancelContextMutex, 0);
|
||||
|
||||
ntStatus = IoCreateSymbolicLink (&Win32NameString, &ntUnicodeString);
|
||||
|
||||
@@ -800,6 +806,177 @@ void RootDeviceControlMutexRelease ()
|
||||
KeReleaseMutex (&RootDeviceControlMutex, FALSE);
|
||||
}
|
||||
|
||||
|
||||
static void RegisterMountCancelContext (PEXTENSION Extension, int nDosDriveNo)
|
||||
{
|
||||
if (!NT_SUCCESS (KeWaitForMutexObject (&MountCancelContextMutex, Executive, KernelMode, FALSE, NULL)))
|
||||
return;
|
||||
|
||||
ActiveMountCancelContext.nDosDriveNo = nDosDriveNo;
|
||||
ActiveMountCancelContext.UserSidLength = 0;
|
||||
InterlockedExchange (&ActiveMountCancelContext.UserSidValid, 0);
|
||||
InterlockedExchange (&ActiveMountCancelContext.UserAbortRequested, 0);
|
||||
InterlockedExchange (&ActiveMountCancelContext.KeyDerivationAbort, 0);
|
||||
InterlockedIncrement (&ActiveMountCancelContext.SequenceNumber);
|
||||
Extension->MountCancelContext = &ActiveMountCancelContext;
|
||||
InterlockedExchange (&ActiveMountCancelContext.Active, 1);
|
||||
|
||||
KeReleaseMutex (&MountCancelContextMutex, FALSE);
|
||||
}
|
||||
|
||||
|
||||
static void SetMountCancelContextUserSid (PEXTENSION Extension, PSID userSid)
|
||||
{
|
||||
ULONG sidLength;
|
||||
|
||||
if (!userSid || Extension->MountCancelContext != &ActiveMountCancelContext)
|
||||
return;
|
||||
|
||||
sidLength = RtlLengthSid (userSid);
|
||||
if (sidLength > sizeof (ActiveMountCancelContext.UserSid))
|
||||
return;
|
||||
|
||||
if (!NT_SUCCESS (KeWaitForMutexObject (&MountCancelContextMutex, Executive, KernelMode, FALSE, NULL)))
|
||||
return;
|
||||
|
||||
if (Extension->MountCancelContext == &ActiveMountCancelContext
|
||||
&& InterlockedExchangeAdd (&ActiveMountCancelContext.Active, 0) != 0
|
||||
&& NT_SUCCESS (RtlCopySid (sizeof (ActiveMountCancelContext.UserSid), ActiveMountCancelContext.UserSid, userSid)))
|
||||
{
|
||||
ActiveMountCancelContext.UserSidLength = sidLength;
|
||||
InterlockedExchange (&ActiveMountCancelContext.UserSidValid, 1);
|
||||
}
|
||||
|
||||
KeReleaseMutex (&MountCancelContextMutex, FALSE);
|
||||
}
|
||||
|
||||
|
||||
static void UnregisterMountCancelContext (PEXTENSION Extension)
|
||||
{
|
||||
if (!NT_SUCCESS (KeWaitForMutexObject (&MountCancelContextMutex, Executive, KernelMode, FALSE, NULL)))
|
||||
return;
|
||||
|
||||
if (Extension->MountCancelContext == &ActiveMountCancelContext)
|
||||
{
|
||||
InterlockedExchange (&ActiveMountCancelContext.Active, 0);
|
||||
InterlockedExchange (&ActiveMountCancelContext.KeyDerivationAbort, 1);
|
||||
InterlockedExchange (&ActiveMountCancelContext.UserSidValid, 0);
|
||||
ActiveMountCancelContext.UserSidLength = 0;
|
||||
Extension->MountCancelContext = NULL;
|
||||
}
|
||||
|
||||
KeReleaseMutex (&MountCancelContextMutex, FALSE);
|
||||
}
|
||||
|
||||
|
||||
static BOOL CurrentUserMatchesSid (PSID userSid)
|
||||
{
|
||||
SECURITY_SUBJECT_CONTEXT subContext;
|
||||
PACCESS_TOKEN accessToken;
|
||||
PTOKEN_USER tokenUser;
|
||||
BOOL result = FALSE;
|
||||
|
||||
if (!userSid)
|
||||
return FALSE;
|
||||
|
||||
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 (userSid, tokenUser->User.Sid);
|
||||
ExFreePool (tokenUser); // Documented in newer versions of WDK
|
||||
|
||||
ret:
|
||||
SeUnlockSubjectContext(&subContext);
|
||||
SeReleaseSubjectContext (&subContext);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
static BOOL CurrentUserCanAbortPendingMount (PSID userSid, BOOL userSidValid)
|
||||
{
|
||||
if (IoIsSystemThread (PsGetCurrentThread()))
|
||||
return TRUE;
|
||||
|
||||
if (UserCanAccessDriveDevice())
|
||||
return TRUE;
|
||||
|
||||
return userSidValid && CurrentUserMatchesSid (userSid);
|
||||
}
|
||||
|
||||
|
||||
static NTSTATUS AbortPendingMount (MOUNT_ABORT_STRUCT *abortRequest)
|
||||
{
|
||||
UCHAR userSid[SECURITY_MAX_SID_SIZE];
|
||||
ULONG userSidLength = 0;
|
||||
LONG sequenceNumber = 0;
|
||||
BOOL userSidValid = FALSE;
|
||||
BOOL activeMountMatches = FALSE;
|
||||
|
||||
if (!NT_SUCCESS (KeWaitForMutexObject (&MountCancelContextMutex, Executive, KernelMode, FALSE, NULL)))
|
||||
return STATUS_UNSUCCESSFUL;
|
||||
|
||||
if (InterlockedExchangeAdd (&ActiveMountCancelContext.Active, 0) != 0
|
||||
&& (abortRequest->nDosDriveNo < 0 || abortRequest->nDosDriveNo == ActiveMountCancelContext.nDosDriveNo))
|
||||
{
|
||||
activeMountMatches = TRUE;
|
||||
sequenceNumber = InterlockedExchangeAdd (&ActiveMountCancelContext.SequenceNumber, 0);
|
||||
userSidValid = InterlockedExchangeAdd (&ActiveMountCancelContext.UserSidValid, 0) != 0;
|
||||
userSidLength = ActiveMountCancelContext.UserSidLength;
|
||||
if (userSidValid && userSidLength <= sizeof (userSid))
|
||||
memcpy (userSid, ActiveMountCancelContext.UserSid, userSidLength);
|
||||
else
|
||||
userSidValid = FALSE;
|
||||
}
|
||||
|
||||
KeReleaseMutex (&MountCancelContextMutex, FALSE);
|
||||
|
||||
if (!activeMountMatches)
|
||||
{
|
||||
abortRequest->nReturnCode = ERR_DRIVE_NOT_FOUND;
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
if (!CurrentUserCanAbortPendingMount (userSidValid ? (PSID) userSid : NULL, userSidValid))
|
||||
{
|
||||
abortRequest->nReturnCode = ERR_ACCESS_DENIED;
|
||||
return STATUS_ACCESS_DENIED;
|
||||
}
|
||||
|
||||
if (!NT_SUCCESS (KeWaitForMutexObject (&MountCancelContextMutex, Executive, KernelMode, FALSE, NULL)))
|
||||
return STATUS_UNSUCCESSFUL;
|
||||
|
||||
if (InterlockedExchangeAdd (&ActiveMountCancelContext.Active, 0) != 0
|
||||
&& sequenceNumber == InterlockedExchangeAdd (&ActiveMountCancelContext.SequenceNumber, 0)
|
||||
&& (abortRequest->nDosDriveNo < 0 || abortRequest->nDosDriveNo == ActiveMountCancelContext.nDosDriveNo))
|
||||
{
|
||||
InterlockedExchange (&ActiveMountCancelContext.UserAbortRequested, 1);
|
||||
InterlockedExchange (&ActiveMountCancelContext.KeyDerivationAbort, 1);
|
||||
abortRequest->nReturnCode = ERR_USER_ABORT;
|
||||
KeReleaseMutex (&MountCancelContextMutex, FALSE);
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
abortRequest->nReturnCode = ERR_DRIVE_NOT_FOUND;
|
||||
KeReleaseMutex (&MountCancelContextMutex, FALSE);
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
/*
|
||||
IOCTL_STORAGE_GET_DEVICE_NUMBER 0x002D1080
|
||||
IOCTL_STORAGE_GET_HOTPLUG_INFO 0x002D0C14
|
||||
@@ -2605,6 +2782,23 @@ NTSTATUS ProcessMainDeviceControlIrp (PDEVICE_OBJECT DeviceObject, PEXTENSION Ex
|
||||
}
|
||||
break;
|
||||
|
||||
case TC_IOCTL_ABORT_MOUNT_VOLUME:
|
||||
if (ValidateIOBufferSize (Irp, sizeof (MOUNT_ABORT_STRUCT), ValidateInputOutput))
|
||||
{
|
||||
MOUNT_ABORT_STRUCT *abortRequest = (MOUNT_ABORT_STRUCT *) Irp->AssociatedIrp.SystemBuffer;
|
||||
|
||||
if (irpSp->Parameters.DeviceIoControl.InputBufferLength != sizeof (MOUNT_ABORT_STRUCT))
|
||||
{
|
||||
Irp->IoStatus.Status = STATUS_INVALID_PARAMETER;
|
||||
Irp->IoStatus.Information = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
Irp->IoStatus.Status = AbortPendingMount (abortRequest);
|
||||
Irp->IoStatus.Information = sizeof (MOUNT_ABORT_STRUCT);
|
||||
}
|
||||
break;
|
||||
|
||||
case TC_IOCTL_MOUNT_VOLUME:
|
||||
if (ValidateIOBufferSize (Irp, sizeof (MOUNT_STRUCT), ValidateInputOutput))
|
||||
{
|
||||
@@ -3216,6 +3410,7 @@ LPWSTR TCTranslateCode (ULONG ulCode)
|
||||
{
|
||||
#define TC_CASE_RET_NAME(CODE) case CODE : return L###CODE
|
||||
|
||||
TC_CASE_RET_NAME (TC_IOCTL_ABORT_MOUNT_VOLUME);
|
||||
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);
|
||||
@@ -4013,6 +4208,8 @@ NTSTATUS MountDevice (PDEVICE_OBJECT DeviceObject, MOUNT_STRUCT *mount)
|
||||
SECURITY_SUBJECT_CONTEXT subContext;
|
||||
PACCESS_TOKEN accessToken;
|
||||
|
||||
RegisterMountCancelContext (NewExtension, mount->nDosDriveNo);
|
||||
|
||||
SeCaptureSubjectContext (&subContext);
|
||||
SeLockSubjectContext(&subContext);
|
||||
if (subContext.ClientToken && subContext.ImpersonationLevel >= SecurityImpersonation)
|
||||
@@ -4033,6 +4230,8 @@ NTSTATUS MountDevice (PDEVICE_OBJECT DeviceObject, MOUNT_STRUCT *mount)
|
||||
{
|
||||
ULONG sidLength = RtlLengthSid (tokenUser->User.Sid);
|
||||
|
||||
SetMountCancelContextUserSid (NewExtension, tokenUser->User.Sid);
|
||||
|
||||
NewExtension->UserSid = TCalloc (sidLength);
|
||||
if (!NewExtension->UserSid)
|
||||
ntStatus = STATUS_INSUFFICIENT_RESOURCES;
|
||||
@@ -4049,6 +4248,8 @@ NTSTATUS MountDevice (PDEVICE_OBJECT DeviceObject, MOUNT_STRUCT *mount)
|
||||
if (NT_SUCCESS (ntStatus))
|
||||
ntStatus = TCStartVolumeThread (NewDeviceObject, NewExtension, mount);
|
||||
|
||||
UnregisterMountCancelContext (NewExtension);
|
||||
|
||||
if (!NT_SUCCESS (ntStatus))
|
||||
{
|
||||
Dump ("Mount FAILURE NT ERROR, ntStatus = 0x%08x\n", ntStatus);
|
||||
|
||||
@@ -26,6 +26,22 @@ typedef struct _THREAD_BLOCK_
|
||||
MOUNT_STRUCT *mount;
|
||||
} THREAD_BLOCK, *PTHREAD_BLOCK;
|
||||
|
||||
#ifndef SECURITY_MAX_SID_SIZE
|
||||
#define SECURITY_MAX_SID_SIZE 68
|
||||
#endif
|
||||
|
||||
typedef struct _MOUNT_CANCEL_CONTEXT_
|
||||
{
|
||||
LONG Active;
|
||||
LONG UserAbortRequested;
|
||||
LONG KeyDerivationAbort;
|
||||
LONG UserSidValid;
|
||||
LONG SequenceNumber;
|
||||
int nDosDriveNo;
|
||||
ULONG UserSidLength;
|
||||
UCHAR UserSid[SECURITY_MAX_SID_SIZE];
|
||||
} MOUNT_CANCEL_CONTEXT, *PMOUNT_CANCEL_CONTEXT;
|
||||
|
||||
|
||||
/* This structure is allocated for non-root devices! WARNING: bRootDevice
|
||||
must be the first member of the structure! */
|
||||
@@ -43,6 +59,7 @@ typedef struct EXTENSION
|
||||
BOOL bThreadShouldQuit; /* Instruct per device worker thread to quit */
|
||||
PETHREAD peThread; /* Thread handle */
|
||||
KEVENT keCreateEvent; /* Device creation event */
|
||||
PMOUNT_CANCEL_CONTEXT MountCancelContext;
|
||||
KSPIN_LOCK ListSpinLock; /* IRP spinlock */
|
||||
LIST_ENTRY ListEntry; /* IRP listentry */
|
||||
KSEMAPHORE RequestSemaphore; /* IRP list request Semaphore */
|
||||
|
||||
+30
-4
@@ -36,6 +36,19 @@
|
||||
|
||||
volatile BOOL ProbingHostDeviceForWrite = FALSE;
|
||||
|
||||
static BOOL MountCancelRequested (PEXTENSION Extension)
|
||||
{
|
||||
PMOUNT_CANCEL_CONTEXT context = Extension->MountCancelContext;
|
||||
return context && InterlockedExchangeAdd (&context->UserAbortRequested, 0) != 0;
|
||||
}
|
||||
|
||||
static void ResetMountKeyDerivationAbort (PEXTENSION Extension)
|
||||
{
|
||||
PMOUNT_CANCEL_CONTEXT context = Extension->MountCancelContext;
|
||||
if (context && !MountCancelRequested (Extension))
|
||||
InterlockedExchange (&context->KeyDerivationAbort, 0);
|
||||
}
|
||||
|
||||
|
||||
NTSTATUS TCOpenVolume (PDEVICE_OBJECT DeviceObject,
|
||||
PEXTENSION Extension,
|
||||
@@ -593,11 +606,20 @@ NTSTATUS TCOpenVolume (PDEVICE_OBJECT DeviceObject,
|
||||
|
||||
/* Attempt to recognize the volume (decrypt the header) */
|
||||
|
||||
if (MountCancelRequested (Extension))
|
||||
{
|
||||
mount->nReturnCode = ERR_USER_ABORT;
|
||||
ntStatus = STATUS_SUCCESS;
|
||||
goto error;
|
||||
}
|
||||
|
||||
ResetMountKeyDerivationAbort (Extension);
|
||||
|
||||
ReadVolumeHeaderRecoveryMode = mount->RecoveryMode;
|
||||
|
||||
if ((volumeType == TC_VOLUME_TYPE_HIDDEN) && mount->bProtectHiddenVolume)
|
||||
{
|
||||
mount->nReturnCode = ReadVolumeHeaderWCache (
|
||||
mount->nReturnCode = ReadVolumeHeaderWCacheWithAbort (
|
||||
FALSE,
|
||||
bAutoCachePassword,
|
||||
mount->bCachePim,
|
||||
@@ -605,11 +627,13 @@ NTSTATUS TCOpenVolume (PDEVICE_OBJECT DeviceObject,
|
||||
&mount->ProtectedHidVolPassword,
|
||||
mount->ProtectedHidVolPkcs5Prf,
|
||||
mount->ProtectedHidVolPim,
|
||||
&tmpCryptoInfo);
|
||||
&tmpCryptoInfo,
|
||||
Extension->MountCancelContext ? &Extension->MountCancelContext->KeyDerivationAbort : NULL,
|
||||
Extension->MountCancelContext ? &Extension->MountCancelContext->UserAbortRequested : NULL);
|
||||
}
|
||||
else
|
||||
{
|
||||
mount->nReturnCode = ReadVolumeHeaderWCache (
|
||||
mount->nReturnCode = ReadVolumeHeaderWCacheWithAbort (
|
||||
mount->bPartitionInInactiveSysEncScope && volumeType == TC_VOLUME_TYPE_NORMAL,
|
||||
bAutoCachePassword,
|
||||
mount->bCachePim,
|
||||
@@ -617,7 +641,9 @@ NTSTATUS TCOpenVolume (PDEVICE_OBJECT DeviceObject,
|
||||
&mount->VolumePassword,
|
||||
mount->pkcs5_prf,
|
||||
mount->VolumePim,
|
||||
&Extension->cryptoInfo);
|
||||
&Extension->cryptoInfo,
|
||||
Extension->MountCancelContext ? &Extension->MountCancelContext->KeyDerivationAbort : NULL,
|
||||
Extension->MountCancelContext ? &Extension->MountCancelContext->UserAbortRequested : NULL);
|
||||
}
|
||||
|
||||
ReadVolumeHeaderRecoveryMode = FALSE;
|
||||
|
||||
Reference in New Issue
Block a user