Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
202 changes: 197 additions & 5 deletions src/FormatDLL/VeraCryptFormatSDK.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@
#include <mutex>
#include <atomic>
#include <Strsafe.h>
#include <array>
#include <Shlwapi.h>

#include "Dlgcode.h"
#include "Crypto.h"
Expand Down Expand Up @@ -73,6 +75,39 @@ extern "C" DWORD GetFormatSectorSize();
void InitApp (HINSTANCE hInstance, wchar_t *lpszCommandLine);
void cleanup ();

constexpr std::array<const wchar_t*, 14> VolumeEncryptionAlgorithmOptions = std::array<const wchar_t*, 14>{
L"AES",
L"Serpent",
L"Twofish",
L"Camellia",
L"Kuznyechik",
L"Serpent(Twofish(AES))",
L"Serpent(AES)",
L"AES(Twofish(Serpent))",
L"Twofish(Serpent)",
L"Camellia(Kuznyechik)",
L"Kuznyechik(Twofish)",
L"Camellia(Serpent)",
L"Kuznyechik(AES)",
L"Kuznyechik(Serpent(Camellia))",
};

constexpr std::array<const wchar_t*, 5> HashAlgorithmOptions = std::array<const wchar_t*, 5>{
L"SHA-512",
L"SHA-256",
L"RIPEMD-160",
L"Whirlpool",
L"BLAKE2s-256"
};

constexpr std::array<const wchar_t*, 5> FileSystemFormatOptions = std::array<const wchar_t*, 5>{
L"NTFS",
L"FAT",
L"ExFAT",
L"ReFS",
L"None"
};

// Global mutex to ensure that volume creation operations are serialized,
// as the underlying code uses extensive global state.
static std::mutex g_sdkMutex;
Expand Down Expand Up @@ -184,15 +219,18 @@ static int CreateVolumeInternal(const VeraCryptFormatOptions* options)

// --- Setup VeraCrypt's global state from our options struct ---
bDevice = options->isDevice;
nVolumeSize = options->size;
if (!options->isDevice) {
nVolumeSize = options->size * (options->sizeMeasureUnity == SizeMeasureUnity::Kilobytes ?
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This multiplication can overflow uint64_t before the later size checks run. For example, a large options->size value with Gigabytes can wrap to a small value and pass subsequent validation incorrectly.

Please validate sizeMeasureUnity, check options->size <= UINT64_MAX / multiplier, and update the public comment because size is no longer always expressed in bytes.

1024 : options->sizeMeasureUnity == SizeMeasureUnity::Megabytes ? 1024 * 1024 : 1024 * 1024 * 1024);
}

nVolumeEA = MapEncryptionAlgorithm(options->encryptionAlgorithm);
nVolumeEA = MapEncryptionAlgorithm(VolumeEncryptionAlgorithmOptions[static_cast<int>(options->encryptionAlgorithm)]);
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This indexes a fixed array using a public enum value without bounds checking. Since this is a C ABI, callers can pass any integer value, not only valid enum constants. An invalid value here can read out of bounds before MapEncryptionAlgorithm has a chance to reject it.

The same issue exists for HashAlgorithmOptions and FileSystemFormatOptions below. Please validate each enum value before indexing, and return the appropriate VCF_ERROR_INVALID_* code.

if (nVolumeEA == 0) return VCF_ERROR_INVALID_ENCRYPTION_ALGORITHM;

hash_algo = MapHashAlgorithm(options->hashAlgorithm);
hash_algo = MapHashAlgorithm(HashAlgorithmOptions[static_cast<int>(options->hashAlgorithm)]);
if (hash_algo == 0) return VCF_ERROR_INVALID_HASH_ALGORITHM;

fileSystem = MapFilesystem(options->filesystem);
fileSystem = MapFilesystem(FileSystemFormatOptions[static_cast<int>(options->filesystem)]);
if (fileSystem == -1) return VCF_ERROR_INVALID_FILESYSTEM;

volumePim = options->pim;
Expand Down Expand Up @@ -332,7 +370,6 @@ static int CreateVolumeInternal(const VeraCryptFormatOptions* options)
}
}


// --- Public DLL Exported Functions ---

extern "C"
Expand Down Expand Up @@ -378,6 +415,161 @@ extern "C"
return CreateVolumeInternal(options);
}

VCF_API int __cdecl VeraCryptMount(const VeraCryptMountOptions* options) {
if (!g_isInitialized)
{
return VCF_ERROR_NOT_INITIALIZED;
}

// Lock the mutex to protect the global state used by VeraCrypt's format code
std::lock_guard<std::mutex> lock(g_sdkMutex);

finally_do({
// Clean up all sensitive data from globals
WipePasswordsAndKeyfiles(true);
// Reset globals to default state
KeyFileRemoveAll(&FirstKeyFile);
});

if (!IsDriveAvailable(static_cast<int>(options->letter))) {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

options is dereferenced here before any null check, and options->path is not validated at all. A null options or null/empty path will crash instead of returning VCF_ERROR_INVALID_PARAMETER.

Please validate options, options->path, and required password/keyfile inputs before using any fields.

return VCF_ERROR_DRIVE_LETTER_UNAVIABLE;
}

MountOptions mountOptions = { 0 };

if (options->password)
{
if (!CheckPasswordLength(NULL, (int)strlen(options->password), options->pim, FALSE, 0, TRUE, TRUE))
{
return VCF_ERROR_PASSWORD_POLICY;
}
strcpy_s((char*)volumePassword.Text, sizeof(volumePassword.Text), options->password);
volumePassword.Length = (unsigned __int32)strlen(options->password);
}
else
{
volumePassword.Text[0] = 0;
volumePassword.Length = 0;
}

if (options->protectedHidVolPassword) {
if (!CheckPasswordLength(NULL, (int)strlen(options->protectedHidVolPassword), options->pim, FALSE, 0, TRUE, TRUE)) {
return VCF_ERROR_PASSWORD_POLICY;
}
strcpy_s((char*)mountOptions.ProtectedHidVolPassword.Text, sizeof(mountOptions.ProtectedHidVolPassword.Text), options->protectedHidVolPassword);
mountOptions.ProtectedHidVolPassword.Length = (unsigned __int32)strlen(options->protectedHidVolPassword);
}
else {
mountOptions.ProtectedHidVolPassword.Text[0] = 0;
mountOptions.ProtectedHidVolPassword.Length = 0;
}

FirstKeyFile = nullptr;
if (options->keyfiles)
{
for (int i = 0; options->keyfiles[i] != nullptr; ++i)
{
KeyFile* kf = (KeyFile*)malloc(sizeof(KeyFile));
if (!kf)
{
KeyFileRemoveAll(&FirstKeyFile);
return VCF_ERROR_OUT_OF_MEMORY;
}
StringCbCopyW(kf->FileName, sizeof(kf->FileName), options->keyfiles[i]);
FirstKeyFile = KeyFileAdd(FirstKeyFile, kf);
}
}

if (!KeyFilesApply(NULL, &volumePassword, FirstKeyFile, NULL))
{
return VCF_ERROR_KEYFILE_ERROR;
}
mountOptions.DisableMountManager = options->DisableMountManager;
StringCbCopyW(mountOptions.Label, sizeof(mountOptions.Label), options->Label);
mountOptions.PartitionInInactiveSysEncScope = options->PartitionInInactiveSysEncScope;
mountOptions.PreserveTimestamp = options->PreserveTimestamp;
mountOptions.ProtectedHidVolPim = options->ProtectedHidVolPim;
mountOptions.ProtectedHidVolPkcs5Prf = options->ProtectedHidVolPkcs5Prf;
mountOptions.ProtectHiddenVolume = options->ProtectHiddenVolume;
mountOptions.ReadOnly = options->ReadOnly;
mountOptions.RecoveryMode = options->RecoveryMode;
mountOptions.Removable = options->Removable;
mountOptions.SkipCachedPasswords = options->SkipCachedPasswords;
mountOptions.UseBackupHeader = options->UseBackupHeader;

int result = MountVolume(NULL, static_cast<int>(options->letter), options->path, &volumePassword,
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

MountVolume can modify the volumePath buffer it receives. Passing options->path directly makes the SDK mutate caller-owned input, and the public struct does not document that requirement.

Please copy options->path into an SDK-owned writable buffer, sized for TC_MAX_PATH, validate/copy safely, and pass that internal buffer to MountVolume.

options->autoDetectEncryptionAlgorithm ? 0 : static_cast<int>(options->encryptionAlgorithm),
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This passes an encryption algorithm enum where MountVolume expects the pkcs5 PRF/hash id. The fifth parameter of MountVolume is int pkcs5, not an encryption algorithm selector.

The mount API should expose a PRF/hash option, or pass 0 for autodetection. Encryption algorithm selection is not the right parameter for this call.

options->pim, options->cachePassword, options->cachePim, options->sharedAccess, &mountOptions,
TRUE, TRUE
);
if (options->sharedAccess)
return result == 2 ? VCF_SUCCESS : result;
return result == 1 ? VCF_SUCCESS : result;
}
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This maps mount failures to VCF_SUCCESS. MountVolume returns 0 for mount failure, including wrong password, and VCF_SUCCESS is also 0. As written, a wrong password can be reported to SDK callers as success.

Please map MountVolume results explicitly: 1 and 2 should map to VCF_SUCCESS, while 0 and -1 must map to nonzero SDK error codes.


VCF_API int __cdecl VeraCryptDismount(DriveLetter letter, BOOL force) {
if (!g_isInitialized)
{
return VCF_ERROR_NOT_INITIALIZED;
}

// Lock the mutex to protect the global state used by VeraCrypt's format code
std::lock_guard<std::mutex> lock(g_sdkMutex);

return UnmountVolume(NULL, static_cast<int>(letter), force) == TRUE ? VCF_SUCCESS : VCF_ERROR_GENERIC;
}

VCF_API int __cdecl GetAbsolutePath(const wchar_t* relativePath, wchar_t* absolutePath, DWORD absolutePathSize)
{
if (!relativePath || !absolutePath || absolutePathSize == 0)
{
SetLastError(ERROR_INVALID_PARAMETER);
return VCF_ERROR_FULL_PATH_GETTING_ERROR;
}

absolutePath[0] = L'\0';

DWORD requiredSize;

requiredSize = GetFullPathNameW(relativePath, 0, NULL, NULL);
if (requiredSize == 0)
{
return VCF_ERROR_FULL_PATH_GETTING_ERROR;
}

if (requiredSize > absolutePathSize)
{
SetLastError(ERROR_INSUFFICIENT_BUFFER);
return VCF_ERROR_FULL_PATH_GETTING_ERROR;
}

DWORD result = GetFullPathNameW(relativePath, absolutePathSize, absolutePath, NULL);
if (result == 0 || result >= absolutePathSize)
{
return VCF_ERROR_FULL_PATH_GETTING_ERROR;
}

return VCF_SUCCESS;
}

VCF_API int __cdecl GetDevicePath(DriveLetter letter, wchar_t* devicePath, DWORD devicePathSize)
{
if (!devicePath || devicePathSize == 0)
{
SetLastError(ERROR_INVALID_PARAMETER);
return VCF_ERROR_FULL_PATH_GETTING_ERROR;
}
devicePath[0] = L'\0';

wchar_t drive[4] = { static_cast<wchar_t>(L'A' + static_cast<int>(letter)), L':', L'\0' };
if (QueryDosDeviceW(drive, devicePath, devicePathSize) == 0)
{
// Error, possibly insufficient buffer
return VCF_ERROR_FULL_PATH_GETTING_ERROR;
}
return VCF_SUCCESS;
}

BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
{
switch (fdwReason)
Expand Down
Loading