From 5e96a5c44c3c78a901d77297517fa4842a297eb9 Mon Sep 17 00:00:00 2001 From: Mounir IDRASSI Date: Sun, 8 Dec 2019 22:51:49 +0100 Subject: Windows: enhancements to the mechanism preserving file timestamps, especially for keyfiles. --- src/Common/Dlgcode.c | 11 +++++--- src/Common/Format.c | 21 +++++++++------ src/Common/Keyfiles.c | 59 ++++++++++++++++------------------------- src/Common/Password.c | 20 +++++++++----- src/Driver/Ntvol.c | 34 +++++++++++++++++++++++- src/ExpandVolume/ExpandVolume.c | 35 ++++++++++++++---------- src/Format/Tcformat.c | 12 +++++++++ src/Mount/Mount.c | 24 ++++++++++------- 8 files changed, 138 insertions(+), 78 deletions(-) diff --git a/src/Common/Dlgcode.c b/src/Common/Dlgcode.c index 47578b27..5a837865 100644 --- a/src/Common/Dlgcode.c +++ b/src/Common/Dlgcode.c @@ -11238,10 +11238,8 @@ int OpenVolume (OpenVolumeContext *context, const wchar_t *volumePath, Password else StringCbCopyW (szCFDevice, sizeof(szCFDevice), szDiskFile); - if (preserveTimestamps) - write = TRUE; - context->HostFileHandle = CreateFile (szCFDevice, GENERIC_READ | (write ? GENERIC_WRITE : 0), FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL); + context->HostFileHandle = CreateFile (szCFDevice, GENERIC_READ | (write ? GENERIC_WRITE : (!context->IsDevice && preserveTimestamps? FILE_WRITE_ATTRIBUTES : 0)), FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL); if (context->HostFileHandle == INVALID_HANDLE_VALUE) { @@ -11263,6 +11261,13 @@ int OpenVolume (OpenVolumeContext *context, const wchar_t *volumePath, Password // Remember the container modification/creation date and time if (!context->IsDevice && preserveTimestamps) { + // ensure that Last Access and Last Write timestamps are not modified + FILETIME ftLastAccessTime; + ftLastAccessTime.dwHighDateTime = 0xFFFFFFFF; + ftLastAccessTime.dwLowDateTime = 0xFFFFFFFF; + + SetFileTime (context->HostFileHandle, NULL, &ftLastAccessTime, NULL); + if (GetFileTime (context->HostFileHandle, &context->CreationTime, &context->LastAccessTime, &context->LastWriteTime) == 0) context->TimestampsValid = FALSE; else diff --git a/src/Common/Format.c b/src/Common/Format.c index bd33f754..82c4df55 100644 --- a/src/Common/Format.c +++ b/src/Common/Format.c @@ -350,6 +350,19 @@ begin_format: nStatus = ERR_OS_ERROR; goto error; } + else if (volParams->hiddenVol && bPreserveTimestamp) + { + // ensure that Last Access and Last Write timestamps are not modified + ftLastAccessTime.dwHighDateTime = 0xFFFFFFFF; + ftLastAccessTime.dwLowDateTime = 0xFFFFFFFF; + + SetFileTime (dev, NULL, &ftLastAccessTime, NULL); + + if (GetFileTime ((HANDLE) dev, &ftCreationTime, &ftLastAccessTime, &ftLastWriteTime) == 0) + bTimeStampValid = FALSE; + else + bTimeStampValid = TRUE; + } DisableFileCompression (dev); @@ -380,14 +393,6 @@ begin_format: } } - if (volParams->hiddenVol && !volParams->bDevice && bPreserveTimestamp) - { - if (GetFileTime ((HANDLE) dev, &ftCreationTime, &ftLastAccessTime, &ftLastWriteTime) == 0) - bTimeStampValid = FALSE; - else - bTimeStampValid = TRUE; - } - if (volParams->hwndDlg && volParams->bGuiMode) KillTimer (volParams->hwndDlg, TIMER_ID_RANDVIEW); /* Volume header */ diff --git a/src/Common/Keyfiles.c b/src/Common/Keyfiles.c index 686f3ca8..174aed92 100644 --- a/src/Common/Keyfiles.c +++ b/src/Common/Keyfiles.c @@ -149,50 +149,41 @@ void KeyFileCloneAll (KeyFile *firstKeyFile, KeyFile **outputKeyFile) static BOOL KeyFileProcess (unsigned __int8 *keyPool, unsigned __int32 keyPoolSize, KeyFile *keyFile) { - FILE *f; unsigned __int8 buffer[64 * 1024]; unsigned __int32 crc = 0xffffffff; unsigned __int32 writePos = 0; - size_t bytesRead, totalRead = 0; + DWORD bytesRead, totalRead = 0; int status = TRUE; - HANDLE src; - FILETIME ftCreationTime; - FILETIME ftLastWriteTime; - FILETIME ftLastAccessTime; - - BOOL bTimeStampValid = FALSE; + BOOL bReadStatus = FALSE; - /* Remember the last access time of the keyfile. It will be preserved in order to prevent - an adversary from determining which file may have been used as keyfile. */ src = CreateFile (keyFile->FileName, - GENERIC_READ | GENERIC_WRITE, + GENERIC_READ | FILE_WRITE_ATTRIBUTES, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL); if (src != INVALID_HANDLE_VALUE) { - if (GetFileTime ((HANDLE) src, &ftCreationTime, &ftLastAccessTime, &ftLastWriteTime)) - bTimeStampValid = TRUE; - } + /* We tell Windows not to update the Last Access timestamp in order to prevent + an adversary from determining which file may have been used as keyfile. */ + FILETIME ftLastAccessTime; + ftLastAccessTime.dwHighDateTime = 0xFFFFFFFF; + ftLastAccessTime.dwLowDateTime = 0xFFFFFFFF; - finally_do_arg (HANDLE, src, + SetFileTime (src, NULL, &ftLastAccessTime, NULL); + } + else { - if (finally_arg != INVALID_HANDLE_VALUE) - CloseHandle (finally_arg); - }); - - f = _wfopen (keyFile->FileName, L"rb"); - if (f == NULL) return FALSE; + /* try to open without FILE_WRITE_ATTRIBUTES in case we are in a ReadOnly filesystem (e.g. CD) */ + src = CreateFile (keyFile->FileName, + GENERIC_READ, + FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL); + if (src == INVALID_HANDLE_VALUE) + return FALSE; + } - while ((bytesRead = fread (buffer, 1, sizeof (buffer), f)) > 0) + while ((bReadStatus = ReadFile (src, buffer, sizeof (buffer), &bytesRead, NULL)) && (bytesRead > 0)) { - size_t i; - - if (ferror (f)) - { - status = FALSE; - goto close; - } + DWORD i; for (i = 0; i < bytesRead; i++) { @@ -211,7 +202,7 @@ static BOOL KeyFileProcess (unsigned __int8 *keyPool, unsigned __int32 keyPoolSi } } - if (ferror (f)) + if (!bReadStatus) { status = FALSE; } @@ -223,13 +214,9 @@ static BOOL KeyFileProcess (unsigned __int8 *keyPool, unsigned __int32 keyPoolSi close: DWORD err = GetLastError(); - fclose (f); - if (bTimeStampValid && !IsFileOnReadOnlyFilesystem (keyFile->FileName)) - { - // Restore the keyfile timestamp - SetFileTime (src, &ftCreationTime, &ftLastAccessTime, &ftLastWriteTime); - } + CloseHandle (src); + burn (buffer, sizeof (buffer)); SetLastError (err); return status; diff --git a/src/Common/Password.c b/src/Common/Password.c index ca0dd468..f2413b6d 100644 --- a/src/Common/Password.c +++ b/src/Common/Password.c @@ -224,6 +224,19 @@ int ChangePwd (const wchar_t *lpszVolume, Password *oldPassword, int old_pkcs5, if (dev == INVALID_HANDLE_VALUE) goto error; + else if (!bDevice && bPreserveTimestamp) + { + // ensure that Last Access and Last Write timestamps are not modified + ftLastAccessTime.dwHighDateTime = 0xFFFFFFFF; + ftLastAccessTime.dwLowDateTime = 0xFFFFFFFF; + + SetFileTime (dev, NULL, &ftLastAccessTime, NULL); + + if (GetFileTime ((HANDLE) dev, &ftCreationTime, &ftLastAccessTime, &ftLastWriteTime) == 0) + bTimeStampValid = FALSE; + else + bTimeStampValid = TRUE; + } if (bDevice) { @@ -313,13 +326,6 @@ int ChangePwd (const wchar_t *lpszVolume, Password *oldPassword, int old_pkcs5, SetRandomPoolEnrichedByUserStatus (FALSE); /* force the display of the random enriching dialog */ - if (!bDevice && bPreserveTimestamp) - { - if (GetFileTime ((HANDLE) dev, &ftCreationTime, &ftLastAccessTime, &ftLastWriteTime) == 0) - bTimeStampValid = FALSE; - else - bTimeStampValid = TRUE; - } for (volumeType = TC_VOLUME_TYPE_NORMAL; volumeType < TC_VOLUME_TYPE_COUNT; volumeType++) { diff --git a/src/Driver/Ntvol.c b/src/Driver/Ntvol.c index c3344fd0..e88105ca 100644 --- a/src/Driver/Ntvol.c +++ b/src/Driver/Ntvol.c @@ -303,7 +303,7 @@ NTSTATUS TCOpenVolume (PDEVICE_OBJECT DeviceObject, if (mount->bMountReadOnly || ntStatus == STATUS_ACCESS_DENIED) { ntStatus = ZwCreateFile (&Extension->hDeviceFile, - GENERIC_READ | SYNCHRONIZE, + GENERIC_READ | (!bRawDevice && mount->bPreserveTimestamp? FILE_WRITE_ATTRIBUTES : 0) | SYNCHRONIZE, &oaFileAttributes, &IoStatusBlock, NULL, @@ -318,6 +318,26 @@ NTSTATUS TCOpenVolume (PDEVICE_OBJECT DeviceObject, NULL, 0); + if (!NT_SUCCESS (ntStatus) && !bRawDevice && mount->bPreserveTimestamp) + { + /* try again without FILE_WRITE_ATTRIBUTES */ + ntStatus = ZwCreateFile (&Extension->hDeviceFile, + GENERIC_READ | SYNCHRONIZE, + &oaFileAttributes, + &IoStatusBlock, + NULL, + FILE_ATTRIBUTE_NORMAL | + FILE_ATTRIBUTE_SYSTEM, + exclusiveAccess ? FILE_SHARE_READ : FILE_SHARE_READ | FILE_SHARE_WRITE, + FILE_OPEN, + FILE_RANDOM_ACCESS | + FILE_WRITE_THROUGH | + (disableBuffering ? FILE_NO_INTERMEDIATE_BUFFERING : 0) | + FILE_SYNCHRONOUS_IO_NONALERT, + NULL, + 0); + } + if (NT_SUCCESS (ntStatus) && !mount->bMountReadOnly) mount->VolumeMountedReadOnlyAfterAccessDenied = TRUE; @@ -362,6 +382,18 @@ NTSTATUS TCOpenVolume (PDEVICE_OBJECT DeviceObject, Extension->fileLastWriteTime = FileBasicInfo.LastWriteTime; Extension->fileLastChangeTime = FileBasicInfo.ChangeTime; Extension->bTimeStampValid = TRUE; + + // we tell the system not to update LastAccessTime, LastWriteTime, and ChangeTime + FileBasicInfo.CreationTime.QuadPart = 0; + FileBasicInfo.LastAccessTime.QuadPart = -1; + FileBasicInfo.LastWriteTime.QuadPart = -1; + FileBasicInfo.ChangeTime.QuadPart = -1; + + ZwSetInformationFile (Extension->hDeviceFile, + &IoStatusBlock, + &FileBasicInfo, + sizeof (FileBasicInfo), + FileBasicInformation); } ntStatus = ZwQueryInformationFile (Extension->hDeviceFile, diff --git a/src/ExpandVolume/ExpandVolume.c b/src/ExpandVolume/ExpandVolume.c index ec78a36f..8db2b573 100644 --- a/src/ExpandVolume/ExpandVolume.c +++ b/src/ExpandVolume/ExpandVolume.c @@ -535,6 +535,27 @@ static int ExpandVolume (HWND hwndDlg, wchar_t *lpszVolume, Password *pVolumePas if (dev == INVALID_HANDLE_VALUE) goto error; + else if (!bDevice && bPreserveTimestamp) + { + // ensure that Last Access and Last Time timestamps are not modified + // in order to preserve plausible deniability of hidden volumes (last password change time is stored in the volume header). + ftLastAccessTime.dwHighDateTime = 0xFFFFFFFF; + ftLastAccessTime.dwLowDateTime = 0xFFFFFFFF; + + SetFileTime (dev, NULL, &ftLastAccessTime, NULL); + + /* Remember the container modification/creation date and time, (used to reset file date and time of + file-hosted volumes after password change (or attempt to), in order to preserve plausible deniability + of hidden volumes (last password change time is stored in the volume header). */ + + if (GetFileTime ((HANDLE) dev, &ftCreationTime, &ftLastAccessTime, &ftLastWriteTime) == 0) + { + bTimeStampValid = FALSE; + MessageBoxW (hwndDlg, GetString ("GETFILETIME_FAILED_PW"), lpszTitle, MB_OK | MB_ICONEXCLAMATION); + } + else + bTimeStampValid = TRUE; + } if (bDevice) { @@ -628,20 +649,6 @@ static int ExpandVolume (HWND hwndDlg, wchar_t *lpszVolume, Password *pVolumePas goto error; } - if (!bDevice && bPreserveTimestamp) - { - /* Remember the container modification/creation date and time, (used to reset file date and time of - file-hosted volumes after password change (or attempt to), in order to preserve plausible deniability - of hidden volumes (last password change time is stored in the volume header). */ - - if (GetFileTime ((HANDLE) dev, &ftCreationTime, &ftLastAccessTime, &ftLastWriteTime) == 0) - { - bTimeStampValid = FALSE; - MessageBoxW (hwndDlg, GetString ("GETFILETIME_FAILED_PW"), lpszTitle, MB_OK | MB_ICONEXCLAMATION); - } - else - bTimeStampValid = TRUE; - } // Seek the volume header headerOffset.QuadPart = TC_VOLUME_HEADER_OFFSET; diff --git a/src/Format/Tcformat.c b/src/Format/Tcformat.c index dba64c20..013e27ca 100644 --- a/src/Format/Tcformat.c +++ b/src/Format/Tcformat.c @@ -3313,6 +3313,12 @@ BOOL IsSparseFile (HWND hwndDlg) if (bPreserveTimestamp) { + FILETIME ftLastAccessTime; + ftLastAccessTime.dwHighDateTime = 0xFFFFFFFF; + ftLastAccessTime.dwLowDateTime = 0xFFFFFFFF; + + SetFileTime (hFile, NULL, &ftLastAccessTime, NULL); + if (GetFileTime (hFile, NULL, &ftLastAccessTime, NULL) == 0) bTimeStampValid = FALSE; else @@ -3352,6 +3358,12 @@ BOOL GetFileVolSize (HWND hwndDlg, unsigned __int64 *size) if (bPreserveTimestamp) { + FILETIME ftLastAccessTime; + ftLastAccessTime.dwHighDateTime = 0xFFFFFFFF; + ftLastAccessTime.dwLowDateTime = 0xFFFFFFFF; + + SetFileTime (hFile, NULL, &ftLastAccessTime, NULL); + if (GetFileTime (hFile, NULL, &ftLastAccessTime, NULL) == 0) bTimeStampValid = FALSE; else diff --git a/src/Mount/Mount.c b/src/Mount/Mount.c index 54dc8158..9130eaee 100644 --- a/src/Mount/Mount.c +++ b/src/Mount/Mount.c @@ -10861,6 +10861,21 @@ int RestoreVolumeHeader (HWND hwndDlg, const wchar_t *lpszVolume) nStatus = ERR_OS_ERROR; goto error; } + else if (!bDevice && bPreserveTimestamp) + { + // ensure that Last Access timestamp is not modified + ftLastAccessTime.dwHighDateTime = 0xFFFFFFFF; + ftLastAccessTime.dwLowDateTime = 0xFFFFFFFF; + + SetFileTime (dev, NULL, &ftLastAccessTime, NULL); + + /* Remember the container modification/creation date and time. */ + + if (GetFileTime ((HANDLE) dev, &ftCreationTime, &ftLastAccessTime, &ftLastWriteTime) == 0) + bTimeStampValid = FALSE; + else + bTimeStampValid = TRUE; + } // Determine volume host size if (bDevice) @@ -10931,15 +10946,6 @@ int RestoreVolumeHeader (HWND hwndDlg, const wchar_t *lpszVolume) hostSize = fileSize.QuadPart; } - if (!bDevice && bPreserveTimestamp) - { - /* Remember the container modification/creation date and time. */ - - if (GetFileTime ((HANDLE) dev, &ftCreationTime, &ftLastAccessTime, &ftLastWriteTime) == 0) - bTimeStampValid = FALSE; - else - bTimeStampValid = TRUE; - } /* Read the volume header from the backup file */ char buffer[TC_VOLUME_HEADER_GROUP_SIZE]; -- cgit v1.2.3