diff --git a/doc/html/en/Command Line Usage for Windows.html b/doc/html/en/Command Line Usage for Windows.html
index b490ec8e..bbfa3e3a 100644
--- a/doc/html/en/Command Line Usage for Windows.html
+++ b/doc/html/en/Command Line Usage for Windows.html
@@ -110,6 +110,10 @@ if it is followed by n or no: don't try to mou
If it is followed by n or no: force the display waiting dialog is displayed while performing operations.
+| /cancelmount |
+Cancels any currently running mount operation and exits without displaying a status message. The process returns exit code 0 when the cancel request is accepted by the driver, or 1 otherwise. |
+
+
| /secureDesktop |
If it is followed by y or yes or if no parameter is specified: display password dialog and token PIN dialog in a dedicated secure desktop to protect against certain types of attacks.
If it is followed by n or no: the password dialog and token PIN dialog are displayed in the normal desktop. |
diff --git a/src/Common/Apidrvr.h b/src/Common/Apidrvr.h
index 81213a27..6b65f90d 100644
--- a/src/Common/Apidrvr.h
+++ b/src/Common/Apidrvr.h
@@ -129,6 +129,8 @@
#define VC_IOCTL_ENCRYPTION_QUEUE_PARAMS TC_IOCTL (43)
+#define TC_IOCTL_ABORT_MOUNT_VOLUME TC_IOCTL (44)
+
// Undocumented IOCTL sent by Windows 10 when handling EFS data on volumes
#define IOCTL_UNKNOWN_WINDOWS10_EFS_ACCESS 0x455610D8
@@ -180,6 +182,12 @@ typedef struct
BOOL VolumeMasterKeyVulnerable;
} MOUNT_STRUCT;
+typedef struct
+{
+ int nDosDriveNo; /* Drive number whose pending mount should be aborted; -1 aborts any pending mount */
+ int nReturnCode; /* Return code back from driver */
+} MOUNT_ABORT_STRUCT;
+
typedef struct
{
int nDosDriveNo; /* Drive letter to unmount */
diff --git a/src/Common/Cache.c b/src/Common/Cache.c
index c3b7ed3a..263ecf32 100644
--- a/src/Common/Cache.c
+++ b/src/Common/Cache.c
@@ -24,6 +24,22 @@ int CachedPim[CACHE_SIZE];
int cacheEmpty = 1;
static int nPasswordIdx = 0;
+static BOOL IsUserAbortRequested (long volatile *pUserAbort)
+{
+ return pUserAbort && *pUserAbort;
+}
+
+static BOOL ResetAbortKeyDerivation (long volatile *pAbortKeyDerivation, long volatile *pUserAbort)
+{
+ if (IsUserAbortRequested (pUserAbort))
+ return FALSE;
+
+ if (pAbortKeyDerivation)
+ *pAbortKeyDerivation = 0;
+
+ return TRUE;
+}
+
uint64 VcGetPasswordEncryptionID (Password* pPassword)
{
return ((uint64) pPassword->Text) + ((uint64) pPassword);
@@ -39,7 +55,7 @@ void VcUnprotectPassword (Password* pPassword, uint64 encID)
VcProtectPassword (pPassword, encID);
}
-int ReadVolumeHeaderWCache (BOOL bBoot, BOOL bCache, BOOL bCachePim, unsigned char *header, Password *password, int pkcs5_prf, int pim, PCRYPTO_INFO *retInfo)
+int ReadVolumeHeaderWCacheWithAbort (BOOL bBoot, BOOL bCache, BOOL bCachePim, unsigned char *header, Password *password, int pkcs5_prf, int pim, PCRYPTO_INFO *retInfo, long volatile *pAbortKeyDerivation, long volatile *pUserAbort)
{
int nReturnCode = ERR_PASSWORD_WRONG;
int i, effectivePim;
@@ -47,7 +63,10 @@ int ReadVolumeHeaderWCache (BOOL bBoot, BOOL bCache, BOOL bCachePim, unsigned ch
/* Attempt to recognize volume using mount password */
if (password->Length > 0)
{
- nReturnCode = ReadVolumeHeader (bBoot, header, password, pkcs5_prf, pim, retInfo, NULL);
+ if (!ResetAbortKeyDerivation (pAbortKeyDerivation, pUserAbort))
+ return ERR_USER_ABORT;
+
+ nReturnCode = ReadVolumeHeaderWithAbort (bBoot, header, password, pkcs5_prf, pim, retInfo, NULL, pAbortKeyDerivation, pUserAbort);
/* Save mount passwords back into cache if asked to do so */
if (bCache && (nReturnCode == 0 || nReturnCode == ERR_CIPHER_INIT_WEAK_KEY))
@@ -113,7 +132,14 @@ int ReadVolumeHeaderWCache (BOOL bBoot, BOOL bCache, BOOL bCachePim, unsigned ch
effectivePim = CachedPim[i];
else
effectivePim = pim;
- nReturnCode = ReadVolumeHeader (bBoot, header, pCurrentPassword, pkcs5_prf, effectivePim, retInfo, NULL);
+
+ if (!ResetAbortKeyDerivation (pAbortKeyDerivation, pUserAbort))
+ {
+ nReturnCode = ERR_USER_ABORT;
+ break;
+ }
+
+ nReturnCode = ReadVolumeHeaderWithAbort (bBoot, header, pCurrentPassword, pkcs5_prf, effectivePim, retInfo, NULL, pAbortKeyDerivation, pUserAbort);
if (nReturnCode != ERR_PASSWORD_WRONG)
break;
@@ -128,6 +154,11 @@ int ReadVolumeHeaderWCache (BOOL bBoot, BOOL bCache, BOOL bCachePim, unsigned ch
return nReturnCode;
}
+int ReadVolumeHeaderWCache (BOOL bBoot, BOOL bCache, BOOL bCachePim, unsigned char *header, Password *password, int pkcs5_prf, int pim, PCRYPTO_INFO *retInfo)
+{
+ return ReadVolumeHeaderWCacheWithAbort (bBoot, bCache, bCachePim, header, password, pkcs5_prf, pim, retInfo, NULL, NULL);
+}
+
void AddPasswordToCache (Password *password, int pim, BOOL bCachePim)
{
diff --git a/src/Common/Cache.h b/src/Common/Cache.h
index 280c73c5..011bdc28 100644
--- a/src/Common/Cache.h
+++ b/src/Common/Cache.h
@@ -23,4 +23,5 @@ extern int cacheEmpty;
void AddPasswordToCache (Password *password, int pim, BOOL bCachePim);
void AddLegacyPasswordToCache (__unaligned PasswordLegacy *password, int pim);
int ReadVolumeHeaderWCache (BOOL bBoot, BOOL bCache, BOOL bCachePim, unsigned char *header, Password *password, int pkcs5_prf, int pim, PCRYPTO_INFO *retInfo);
+int ReadVolumeHeaderWCacheWithAbort (BOOL bBoot, BOOL bCache, BOOL bCachePim, unsigned char *header, Password *password, int pkcs5_prf, int pim, PCRYPTO_INFO *retInfo, long volatile *pAbortKeyDerivation, long volatile *pUserAbort);
void WipeCache (void);
diff --git a/src/Common/Common.rc b/src/Common/Common.rc
index 640895a2..f6196905 100644
--- a/src/Common/Common.rc
+++ b/src/Common/Common.rc
@@ -338,8 +338,9 @@ STYLE DS_SETFONT | DS_MODALFRAME | DS_SETFOREGROUND | DS_FIXEDSYS | DS_CENTER |
CAPTION "VeraCrypt"
FONT 8, "MS Shell Dlg", 0, 0, 0x0
BEGIN
- CTEXT "Please wait...\nThis process may take a long time and VeraCrypt may seem unresponsive.",IDT_STATIC_MODAL_WAIT_DLG_INFO,9,11,274,33
- CONTROL "",IDC_WAIT_PROGRESS_BAR,"msctls_progress32",WS_BORDER,7,49,278,14
+ CTEXT "Please wait...\nThis process may take a long time and VeraCrypt may seem unresponsive.",IDT_STATIC_MODAL_WAIT_DLG_INFO,9,7,274,30
+ CONTROL "",IDC_WAIT_PROGRESS_BAR,"msctls_progress32",WS_BORDER,7,41,278,12
+ PUSHBUTTON "Cancel",IDCANCEL,121,56,50,14
END
IDD_TEXT_EDIT_DLG DIALOGEX 0, 0, 372, 220
diff --git a/src/Common/Dlgcode.c b/src/Common/Dlgcode.c
index 8915578a..e54ee739 100644
--- a/src/Common/Dlgcode.c
+++ b/src/Common/Dlgcode.c
@@ -8880,12 +8880,18 @@ BOOL GetPhysicalDriveStorageInformation(UINT nDriveNumber, STORAGE_ACCESS_ALIGNM
// implementation of the generic wait dialog mechanism
static UINT g_wmWaitDlg = ::RegisterWindowMessage(L"VeraCryptWaitDlgMessage");
+#define WAIT_DLG_CANCEL_RETRY_TIMER_ID 1
+#define WAIT_DLG_CANCEL_RETRY_INTERVAL 250
+
+typedef BOOL (CALLBACK* WaitCancelProc)(void* pArg, HWND hWaitDlg);
typedef struct
{
HWND hwnd;
void* pArg;
WaitThreadProc callback;
+ WaitCancelProc cancelCallback;
+ BOOL cancelRequested;
} WaitThreadParam;
static void _cdecl WaitThread (void* pParam)
@@ -8899,6 +8905,22 @@ static void _cdecl WaitThread (void* pParam)
PostMessage (pThreadParam->hwnd, g_wmWaitDlg, 0, 0);
}
+static BOOL WaitDlgTryCancel (HWND hwndDlg, WaitThreadParam* thParam)
+{
+ if (thParam && thParam->cancelCallback)
+ {
+ if (thParam->cancelCallback (thParam->pArg, hwndDlg))
+ {
+ KillTimer (hwndDlg, WAIT_DLG_CANCEL_RETRY_TIMER_ID);
+ return TRUE;
+ }
+
+ SetTimer (hwndDlg, WAIT_DLG_CANCEL_RETRY_TIMER_ID, WAIT_DLG_CANCEL_RETRY_INTERVAL, NULL);
+ }
+
+ return FALSE;
+}
+
BOOL CALLBACK WaitDlgProc (HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam)
{
WORD lw = LOWORD (wParam);
@@ -8908,6 +8930,7 @@ BOOL CALLBACK WaitDlgProc (HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam)
case WM_INITDIALOG:
{
WaitThreadParam* thParam = (WaitThreadParam*) lParam;
+ SetWindowLongPtr (hwndDlg, DWLP_USER, (LONG_PTR) thParam);
// set the progress bar type to MARQUEE (indefinite progress)
HWND hProgress = GetDlgItem (hwndDlg, IDC_WAIT_PROGRESS_BAR);
@@ -8935,24 +8958,50 @@ BOOL CALLBACK WaitDlgProc (HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam)
}
LocalizeDialog (hwndDlg, NULL);
+ if (!thParam->cancelCallback)
+ ShowWindow (GetDlgItem (hwndDlg, IDCANCEL), SW_HIDE);
_beginthread(WaitThread, 0, thParam);
return 0;
}
case WM_COMMAND:
- if (lw == IDOK || lw == IDCANCEL)
+ if (lw == IDCANCEL)
+ {
+ WaitThreadParam* thParam = (WaitThreadParam*) GetWindowLongPtr (hwndDlg, DWLP_USER);
+ if (thParam && thParam->cancelCallback && !thParam->cancelRequested)
+ {
+ thParam->cancelRequested = TRUE;
+ EnableWindow (GetDlgItem (hwndDlg, IDCANCEL), FALSE);
+ WaitDlgTryCancel (hwndDlg, thParam);
+ }
+ return 1;
+ }
+
+ if (lw == IDOK)
return 1;
else
return 0;
+ case WM_TIMER:
+ if (wParam == WAIT_DLG_CANCEL_RETRY_TIMER_ID)
+ {
+ WaitThreadParam* thParam = (WaitThreadParam*) GetWindowLongPtr (hwndDlg, DWLP_USER);
+ if (thParam && thParam->cancelRequested)
+ WaitDlgTryCancel (hwndDlg, thParam);
+ return 1;
+ }
+ return 0;
+
case WM_DESTROY:
+ KillTimer (hwndDlg, WAIT_DLG_CANCEL_RETRY_TIMER_ID);
DetachProtectionFromCurrentThread();
return 0;
default:
if (msg == g_wmWaitDlg)
{
+ KillTimer (hwndDlg, WAIT_DLG_CANCEL_RETRY_TIMER_ID);
EndDialog (hwndDlg, IDOK);
return 1;
}
@@ -9015,11 +9064,13 @@ static LRESULT CALLBACK ShowWaitDialogParentWndProc (HWND hWnd, UINT message, WP
}
-void ShowWaitDialog(HWND hwnd, BOOL bUseHwndAsParent, WaitThreadProc callback, void* pArg)
+static void ShowWaitDialogEx(HWND hwnd, BOOL bUseHwndAsParent, WaitThreadProc callback, WaitCancelProc cancelCallback, void* pArg)
{
BOOL bEffectiveHideWaitingDialog = bCmdHideWaitingDialogValid? bCmdHideWaitingDialog : bHideWaitingDialog;
WaitThreadParam threadParam;
threadParam.callback = callback;
+ threadParam.cancelCallback = cancelCallback;
+ threadParam.cancelRequested = FALSE;
threadParam.pArg = pArg;
if (WaitDialogDisplaying || bEffectiveHideWaitingDialog)
@@ -9082,6 +9133,11 @@ void ShowWaitDialog(HWND hwnd, BOOL bUseHwndAsParent, WaitThreadProc callback, v
}
}
+void ShowWaitDialog(HWND hwnd, BOOL bUseHwndAsParent, WaitThreadProc callback, void* pArg)
+{
+ ShowWaitDialogEx (hwnd, bUseHwndAsParent, callback, NULL, pArg);
+}
+
#ifndef SETUP
/************************************************************************/
@@ -9108,6 +9164,26 @@ static BOOL PerformMountIoctl (MOUNT_STRUCT* pmount, LPDWORD pdwResult, BOOL use
sizeof (MOUNT_STRUCT), pmount, sizeof (MOUNT_STRUCT), pdwResult, NULL);
}
+BOOL AbortMountOperation (int nDosDriveNo)
+{
+ BOOL bResult;
+ DWORD dwResult;
+ HANDLE hAbortDriver = CreateFile (WIN32_ROOT_PREFIX, 0, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL);
+ MOUNT_ABORT_STRUCT abortMount;
+
+ if (hAbortDriver == INVALID_HANDLE_VALUE)
+ return FALSE;
+
+ memset (&abortMount, 0, sizeof (abortMount));
+ abortMount.nDosDriveNo = nDosDriveNo;
+
+ bResult = DeviceIoControl (hAbortDriver, TC_IOCTL_ABORT_MOUNT_VOLUME, &abortMount,
+ sizeof (abortMount), &abortMount, sizeof (abortMount), &dwResult, NULL);
+
+ CloseHandle (hAbortDriver);
+ return bResult && abortMount.nReturnCode == ERR_USER_ABORT;
+}
+
// specific definitions and implementation for support of mount operation
// in wait dialog mechanism
@@ -9130,6 +9206,13 @@ void CALLBACK MountWaitThreadProc(void* pArg, HWND )
pThreadParam->dwLastError = GetLastError ();
}
+BOOL CALLBACK MountWaitCancelProc(void* pArg, HWND )
+{
+ MountThreadParam* pThreadParam = (MountThreadParam*) pArg;
+
+ return AbortMountOperation (pThreadParam->pmount->nDosDriveNo);
+}
+
/************************************************************************/
// Use only cached passwords if password = NULL
@@ -9366,7 +9449,7 @@ retry:
mountThreadParam.pdwResult = &dwResult;
mountThreadParam.dwLastError = ERROR_SUCCESS;
- ShowWaitDialog (hwndDlg, FALSE, MountWaitThreadProc, &mountThreadParam);
+ ShowWaitDialogEx (hwndDlg, FALSE, MountWaitThreadProc, MountWaitCancelProc, &mountThreadParam);
dwLastError = mountThreadParam.dwLastError;
}
@@ -9428,6 +9511,9 @@ retry:
if (mount.nReturnCode != 0)
{
+ if (mount.nReturnCode == ERR_USER_ABORT)
+ return -1;
+
if (mount.nReturnCode == ERR_PASSWORD_WRONG)
{
// Do not report wrong password, if not instructed to
diff --git a/src/Common/Dlgcode.h b/src/Common/Dlgcode.h
index e249c5d1..f4308b8b 100644
--- a/src/Common/Dlgcode.h
+++ b/src/Common/Dlgcode.h
@@ -412,6 +412,7 @@ BOOL IsDriveAvailable (int driveNo);
BOOL IsDeviceMounted (wchar_t *deviceName);
int DriverUnmountVolume (HWND hwndDlg, int nDosDriveNo, BOOL forced);
void BroadcastDeviceChange (WPARAM message, int nDosDriveNo, DWORD driveMap);
+BOOL AbortMountOperation (int nDosDriveNo);
int MountVolume (HWND hwndDlg, int driveNo, wchar_t *volumePath, Password *password, int pkcs5, int pim, BOOL cachePassword, BOOL cachePim, BOOL sharedAccess, const MountOptions* const mountOptions, BOOL quiet, BOOL bReportWrongPassword);
BOOL UnmountVolume (HWND hwndDlg , int nDosDriveNo, BOOL forceUnmount);
BOOL UnmountVolumeAfterFormatExCall (HWND hwndDlg, int nDosDriveNo);
diff --git a/src/Common/Volumes.c b/src/Common/Volumes.c
index 495bbc0b..9d1c5d04 100644
--- a/src/Common/Volumes.c
+++ b/src/Common/Volumes.c
@@ -186,7 +186,7 @@ static int MapArgon2ResultToVcError (int result)
BOOL ReadVolumeHeaderRecoveryMode = FALSE;
-int ReadVolumeHeader (BOOL bBoot, unsigned char *encryptedHeader, Password *password, int selected_pkcs5_prf, int pim, PCRYPTO_INFO *retInfo, CRYPTO_INFO *retHeaderCryptoInfo)
+int ReadVolumeHeaderWithAbort (BOOL bBoot, unsigned char *encryptedHeader, Password *password, int selected_pkcs5_prf, int pim, PCRYPTO_INFO *retInfo, CRYPTO_INFO *retHeaderCryptoInfo, long volatile *pAbortKeyDerivation, long volatile *pUserAbort)
{
unsigned char header[TC_VOLUME_HEADER_EFFECTIVE_SIZE];
unsigned char* keyInfoBuffer = NULL;
@@ -203,6 +203,7 @@ int ReadVolumeHeader (BOOL bBoot, unsigned char *encryptedHeader, Password *pass
int iterationsCount = 0;
int memoryCost = 0;
LONG volatile abortKeyDerivation = 0;
+ LONG volatile *effectiveAbortKeyDerivation = pAbortKeyDerivation ? (LONG volatile *) pAbortKeyDerivation : &abortKeyDerivation;
#ifndef VC_DCS_DISABLE_ARGON2
int lastArgon2DerivationResult = 0;
#endif
@@ -326,6 +327,12 @@ int ReadVolumeHeader (BOOL bBoot, unsigned char *encryptedHeader, Password *pass
// Test all available PKCS5 PRFs
for (enqPkcs5Prf = FIRST_PRF_ID; enqPkcs5Prf <= LAST_PRF_ID || queuedWorkItems > 0; ++enqPkcs5Prf)
{
+ if (pUserAbort && *pUserAbort)
+ {
+ status = ERR_USER_ABORT;
+ goto err;
+ }
+
// if a PRF is specified, we skip all other PRFs
if (selected_pkcs5_prf != 0 && enqPkcs5Prf != selected_pkcs5_prf)
continue;
@@ -355,7 +362,7 @@ int ReadVolumeHeader (BOOL bBoot, unsigned char *encryptedHeader, Password *pass
iterationsCount = get_pkcs5_iteration_count (enqPkcs5Prf, pim, bBoot, &memoryCost);
EncryptionThreadPoolBeginKeyDerivation (keyDerivationCompletedEvent, noOutstandingWorkItemEvent,
&item->KeyReady, outstandingWorkItemCount, enqPkcs5Prf, keyInfo->userKey,
- keyInfo->keyLength, keyInfo->salt, iterationsCount, memoryCost, item->DerivedKey, &item->DerivationResult, &abortKeyDerivation);
+ keyInfo->keyLength, keyInfo->salt, iterationsCount, memoryCost, item->DerivedKey, &item->DerivationResult, effectiveAbortKeyDerivation);
++queuedWorkItems;
break;
@@ -376,6 +383,12 @@ int ReadVolumeHeader (BOOL bBoot, unsigned char *encryptedHeader, Password *pass
item = &keyDerivationWorkItems[i];
if (!item->Free && InterlockedExchangeAdd (&item->KeyReady, 0) == TRUE)
{
+ if (pUserAbort && *pUserAbort)
+ {
+ status = ERR_USER_ABORT;
+ goto err;
+ }
+
LONG derivationResult = InterlockedExchangeAdd (&item->DerivationResult, 0);
if (derivationResult != 0)
{
@@ -418,29 +431,29 @@ KeyReady: ;
{
case SHA512:
derive_key_sha512 (keyInfo->userKey, keyInfo->keyLength, keyInfo->salt,
- PKCS5_SALT_SIZE, keyInfo->noIterations, dk, GetMaxPkcs5OutSize(), &abortKeyDerivation);
+ PKCS5_SALT_SIZE, keyInfo->noIterations, dk, GetMaxPkcs5OutSize(), effectiveAbortKeyDerivation);
break;
case SHA256:
derive_key_sha256 (keyInfo->userKey, keyInfo->keyLength, keyInfo->salt,
- PKCS5_SALT_SIZE, keyInfo->noIterations, dk, GetMaxPkcs5OutSize(), &abortKeyDerivation);
+ PKCS5_SALT_SIZE, keyInfo->noIterations, dk, GetMaxPkcs5OutSize(), effectiveAbortKeyDerivation);
break;
#ifndef WOLFCRYPT_BACKEND
case BLAKE2S:
derive_key_blake2s (keyInfo->userKey, keyInfo->keyLength, keyInfo->salt,
- PKCS5_SALT_SIZE, keyInfo->noIterations, dk, GetMaxPkcs5OutSize(), &abortKeyDerivation);
+ PKCS5_SALT_SIZE, keyInfo->noIterations, dk, GetMaxPkcs5OutSize(), effectiveAbortKeyDerivation);
break;
case WHIRLPOOL:
derive_key_whirlpool (keyInfo->userKey, keyInfo->keyLength, keyInfo->salt,
- PKCS5_SALT_SIZE, keyInfo->noIterations, dk, GetMaxPkcs5OutSize(), &abortKeyDerivation);
+ PKCS5_SALT_SIZE, keyInfo->noIterations, dk, GetMaxPkcs5OutSize(), effectiveAbortKeyDerivation);
break;
case STREEBOG:
derive_key_streebog(keyInfo->userKey, keyInfo->keyLength, keyInfo->salt,
- PKCS5_SALT_SIZE, keyInfo->noIterations, dk, GetMaxPkcs5OutSize(), &abortKeyDerivation);
+ PKCS5_SALT_SIZE, keyInfo->noIterations, dk, GetMaxPkcs5OutSize(), effectiveAbortKeyDerivation);
break;
@@ -448,7 +461,7 @@ KeyReady: ;
case ARGON2:
{
int derivationResult = derive_key_argon2(keyInfo->userKey, keyInfo->keyLength, keyInfo->salt,
- PKCS5_SALT_SIZE, keyInfo->noIterations, keyInfo->memoryCost, dk, ARGON2_HEADER_KEYDATA_SIZE, &abortKeyDerivation);
+ PKCS5_SALT_SIZE, keyInfo->noIterations, keyInfo->memoryCost, dk, ARGON2_HEADER_KEYDATA_SIZE, effectiveAbortKeyDerivation);
if (derivationResult != 0)
{
if (selected_pkcs5_prf == 0)
@@ -470,6 +483,12 @@ KeyReady: ;
}
}
+ if (pUserAbort && *pUserAbort)
+ {
+ status = ERR_USER_ABORT;
+ goto err;
+ }
+
// Test all available modes of operation
for (cryptoInfo->mode = FIRST_MODE_OF_OPERATION_ID;
cryptoInfo->mode <= LAST_MODE_OF_OPERATION;
@@ -677,7 +696,7 @@ KeyReady: ;
if ((selected_pkcs5_prf == 0) && (encryptionThreadCount > 1))
{
// Signal other threads to stop
- InterlockedExchange(&abortKeyDerivation, 1);
+ InterlockedExchange(effectiveAbortKeyDerivation, 1);
}
#endif
goto ret;
@@ -689,12 +708,15 @@ KeyReady: ;
status = MapArgon2ResultToVcError (lastArgon2DerivationResult);
else
#endif
+ if (pUserAbort && *pUserAbort)
+ status = ERR_USER_ABORT;
+ else
status = ERR_PASSWORD_WRONG;
err:
#if !defined(_UEFI)
// Signal threads to stop
- InterlockedExchange(&abortKeyDerivation, 1);
+ InterlockedExchange(effectiveAbortKeyDerivation, 1);
#endif
if (cryptoInfo != retHeaderCryptoInfo)
{
@@ -744,6 +766,11 @@ ret:
return status;
}
+int ReadVolumeHeader (BOOL bBoot, unsigned char *encryptedHeader, Password *password, int selected_pkcs5_prf, int pim, PCRYPTO_INFO *retInfo, CRYPTO_INFO *retHeaderCryptoInfo)
+{
+ return ReadVolumeHeaderWithAbort (bBoot, encryptedHeader, password, selected_pkcs5_prf, pim, retInfo, retHeaderCryptoInfo, NULL, NULL);
+}
+
#if defined(_WIN32) && !defined(_UEFI)
void ComputeBootloaderFingerprint (uint8 *bootLoaderBuf, unsigned int bootLoaderSize, uint8* fingerprint)
{
diff --git a/src/Common/Volumes.h b/src/Common/Volumes.h
index 02e3e7f7..3b642cfb 100644
--- a/src/Common/Volumes.h
+++ b/src/Common/Volumes.h
@@ -152,6 +152,7 @@ int CreateVolumeHeaderInMemory(BOOL bBoot, unsigned char *encryptedHeader, int e
BOOL RandgetBytes(unsigned char *buf, int len, BOOL forceSlowPoll);
#else
int ReadVolumeHeader (BOOL bBoot, unsigned char *encryptedHeader, Password *password, int pkcs5_prf, int pim, PCRYPTO_INFO *retInfo, CRYPTO_INFO *retHeaderCryptoInfo);
+int ReadVolumeHeaderWithAbort (BOOL bBoot, unsigned char *encryptedHeader, Password *password, int pkcs5_prf, int pim, PCRYPTO_INFO *retInfo, CRYPTO_INFO *retHeaderCryptoInfo, long volatile *pAbortKeyDerivation, long volatile *pUserAbort);
#if defined(_WIN32) && !defined(_UEFI)
void ComputeBootloaderFingerprint (uint8 *bootLoaderBuf, unsigned int bootLoaderSize, uint8* fingerprint);
#endif
diff --git a/src/Driver/Ntdriver.c b/src/Driver/Ntdriver.c
index cf0150f9..aa56c98e 100644
--- a/src/Driver/Ntdriver.c
+++ b/src/Driver/Ntdriver.c
@@ -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);
diff --git a/src/Driver/Ntdriver.h b/src/Driver/Ntdriver.h
index 82253146..8cbb3127 100644
--- a/src/Driver/Ntdriver.h
+++ b/src/Driver/Ntdriver.h
@@ -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 */
diff --git a/src/Driver/Ntvol.c b/src/Driver/Ntvol.c
index 19cc0788..16866a9c 100644
--- a/src/Driver/Ntvol.c
+++ b/src/Driver/Ntvol.c
@@ -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;
diff --git a/src/Mount/Mount.c b/src/Mount/Mount.c
index 964314b5..a2d22457 100644
--- a/src/Mount/Mount.c
+++ b/src/Mount/Mount.c
@@ -103,6 +103,7 @@ enum hidden_os_read_only_notif_mode
#define TIMER_INTERVAL_KEYB_LAYOUT_GUARD 10
#define TIMER_INTERVAL_UPDATE_DEVICE_LIST 1000
#define TIMER_INTERVAL_CHECK_FOREGROUND 500
+#define TC_COMMAND_CANCEL_MOUNT L"/cancelmount"
BootEncryption *BootEncObj = NULL;
BootEncryptionStatus BootEncStatus;
@@ -5502,7 +5503,7 @@ static BOOL Mount (HWND hwndDlg, int nDosDriveNo, wchar_t *szVolFileName, int pi
NormalCursor ();
- if (mounted)
+ if (mounted > 0)
{
// Check for problematic file extensions (exe, dll, sys)
@@ -9558,6 +9559,7 @@ void ExtractCommandLine (HWND hwndDlg, wchar_t *lpszCommandLine)
OptionEnableMemoryProtection,
OptionEnableScreenProtection,
OptionSignalExit,
+ CommandCancelMount,
CommandUnmount,
};
@@ -9591,6 +9593,8 @@ void ExtractCommandLine (HWND hwndDlg, wchar_t *lpszCommandLine)
{ OptionEnableMemoryProtection, L"/protectMemory", NULL, FALSE },
{ OptionEnableScreenProtection, L"/protectScreen", NULL, FALSE },
{ OptionSignalExit, L"/signalExit", NULL, FALSE },
+ // Add /cancelmount to the command table so it appears in /help.
+ { CommandCancelMount, TC_COMMAND_CANCEL_MOUNT, NULL, FALSE },
{ CommandUnmount, L"/unmount", L"/u", FALSE },
};
@@ -10588,6 +10592,12 @@ int WINAPI wWinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, wchar_t *lpsz
for (int i = 0; argv && i < argc; i++)
{
+ if (_wcsicmp (argv[i], TC_COMMAND_CANCEL_MOUNT) == 0)
+ {
+ BOOL abortSent = AbortMountOperation (-1);
+ LocalFree (argv); // free memory allocated by CommandLineToArgvW
+ return abortSent ? 0 : 1;
+ }
if (_wcsicmp (argv[i], L"/protectScreen") == 0)
{
if ((i < argc - 1) && _wcsicmp (argv[i + 1], L"no") == 0)