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)