1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182
|
/*
** The Sleuth Kit
**
** Brian Carrier [carrier <at> sleuthkit [dot] org]
** Copyright (c) 2024 Sleuth Kit Labs, LLC. All Rights reserved
** Copyright (c) 2010-2021 Brian Carrier. All Rights reserved
**
** This software is distributed under the Common Public License 1.0
*/
// Methods to handle volume encryption (currently only BitLocker is supported)
#include "encryptionHelper.h"
#ifdef HAVE_LIBMBEDTLS
#include "tsk/util/Bitlocker/BitlockerParser.h"
#endif
/**
* Test whether the volume is encrypted with BitLocker and initialize the parser and other fields if it is.
*
* The theory behind the return values is that we want to get the wrong password / needs password messages back
* to the user, which means we don't want to overwrite it with any other error codes.
*
* @param a_fs_info The TSK_FS_INFO object. Should have the img_info and volume offset set but can otherwise be uninitialized.
* Will be updated if we find an successfully initialize BitLocker.
* @param a_pass The password or recovery password to use for decryption. May be empty. If the password is not needed
* (for example if we have clear key) it will be ignored.
*
* @return 0 if:
* - We didn't find the Bitlocker signature
* - We found encryption and did all the initialization successfully
* - We found encryption but had an unspecified error in initialization
* Returns -1 if:
* - We got far enough to be confident that it's Bitlocker and have a specific error message to get back to the user
*/
int handleBitlocker(TSK_FS_INFO* a_fs_info, const char* a_pass) {
#ifdef HAVE_LIBMBEDTLS
BitlockerParser* bitlockerParser = new BitlockerParser();
BITLOCKER_STATUS status = bitlockerParser->initialize(a_fs_info->img_info, a_fs_info->offset, a_pass);
if (status == BITLOCKER_STATUS::NOT_BITLOCKER) {
delete bitlockerParser;
return 0;
}
if (status != BITLOCKER_STATUS::SUCCESS) {
// If we have some specific error cases we want to get that information back to the user
if (status == BITLOCKER_STATUS::WRONG_PASSWORD) {
tsk_error_reset();
tsk_error_set_errno(TSK_ERR_FS_BITLOCKER_ERROR);
string errStr = "Incorrect password entered " + bitlockerParser->getRecoveryKeyIdStr();
tsk_error_set_errstr(errStr.c_str());
delete bitlockerParser;
return -1;
} else if (status == BITLOCKER_STATUS::NEED_PASSWORD) {
tsk_error_reset();
tsk_error_set_errno(TSK_ERR_FS_BITLOCKER_ERROR);
string errStr = "Password required to decrypt volume " + bitlockerParser->getRecoveryKeyIdStr();
tsk_error_set_errstr(errStr.c_str());
delete bitlockerParser;
return -1;
}
else if (status == BITLOCKER_STATUS::UNSUPPORTED_KEY_PROTECTION_TYPE) {
string message = "Unsupported key protection type(s): " + bitlockerParser->getUnsupportedProtectionTypes();
tsk_error_reset();
tsk_error_set_errno(TSK_ERR_FS_BITLOCKER_ERROR);
tsk_error_set_errstr(message.c_str());
delete bitlockerParser;
return -1;
}
// It's unlikely we're going to be able to open the file system (we found at least one BitLocker header) but it's safer to try
delete bitlockerParser;
return 0;
}
// Store the BitLocker data to use when reading the volume
a_fs_info->encryption_type = TSK_FS_ENCRYPTION_TYPE_BITLOCKER;
a_fs_info->encryption_data = (void*)bitlockerParser;
a_fs_info->flags = (TSK_FS_INFO_FLAG_ENUM)(a_fs_info->flags | TSK_FS_INFO_FLAG_ENCRYPTED);
a_fs_info->block_size = bitlockerParser->getSectorSize();
// We don't set a_fs_info->decrypt_block here because Bitlocker needs to handle both reading in the block
// and doing the decryption since some sectors may have been relocated
#endif
return 0;
}
/**
* Check if the volume appears to be encrypted and attempt to initialize the encryption object.
*
* @return 0 if:
* - There was no encryption found
* - We found encryption and did all the initialization successfully
* - We found encryption but had an unspecified error in initialization
* Returns -1 if:
* - We found encryption and got far enough that we're confident we should not continue trying to parse the file system and
* have potentially useful feedback to give the user (like that the password was incorrect)
*/
int handleVolumeEncryption(TSK_FS_INFO* a_fs_info, const char* a_pass) {
int ret = 0;
#ifdef HAVE_LIBMBEDTLS
ret = handleBitlocker(a_fs_info, a_pass);
#endif
return ret;
}
/**
* Reads and decrypts one or more sectors starting at the given offset.
* The offset is expected to be sector-aligned and the length should be a multiple of the sector size.
*
* @param a_fs_info The TSK_FS_INFO object
* @param offsetInVolume Offset to start reading at (relative to the start of the volume)
* @param len Number of bytes to read
* @param data Will hold decrypted data
*
* @return Number of bytes read or -1 on error
*/
#ifdef HAVE_LIBMBEDTLS
ssize_t read_and_decrypt_bitlocker_blocks(TSK_FS_INFO* a_fs_info, TSK_DADDR_T offsetInVolume, size_t len, void* data) {
if (a_fs_info->encryption_type != TSK_FS_ENCRYPTION_TYPE_ENUM::TSK_FS_ENCRYPTION_TYPE_BITLOCKER
|| a_fs_info->encryption_data == NULL
|| data == NULL) {
return -1;
}
if (len == 0) {
return 0;
}
BitlockerParser* parser = (BitlockerParser*)a_fs_info->encryption_data;
return parser->readAndDecryptSectors(offsetInVolume, len, (uint8_t*)data);
}
#endif
/**
* Copys a summary of the encryption algoritm to a_desc. Expected size of description is under 100 characters.
*
* @param a_fs_info TSK_FS_INFO object
* @param a_desc Output buffer for description
* @param a_descLen Size of output buffer (recommended - 256 bytes)
*/
void tsk_fs_get_encryption_description(TSK_FS_INFO* a_fs_info, char* a_desc, size_t a_descLen) {
if (a_descLen <= 0) {
return;
}
memset(a_desc, 0, a_descLen);
#ifdef HAVE_LIBMBEDTLS
if (a_fs_info->encryption_type == TSK_FS_ENCRYPTION_TYPE_ENUM::TSK_FS_ENCRYPTION_TYPE_BITLOCKER
&& a_fs_info->encryption_data != NULL) {
BitlockerParser* parser = (BitlockerParser*)a_fs_info->encryption_data;
string descStr = parser->getDescription();
strncpy(a_desc, descStr.c_str(), a_descLen - 1);
}
#endif
}
/**
* Free any memory being held by encryption objects
*
* @param a_fs_info The TSK_FS_INFO object
*/
void freeEncryptionData(TSK_FS_INFO* a_fs_info) {
#ifdef HAVE_LIBMBEDTLS
if (a_fs_info->encryption_type == TSK_FS_ENCRYPTION_TYPE_ENUM::TSK_FS_ENCRYPTION_TYPE_BITLOCKER
&& a_fs_info->encryption_data != NULL) {
BitlockerParser* parser = (BitlockerParser*)a_fs_info->encryption_data;
delete parser;
a_fs_info->encryption_data = NULL;
}
a_fs_info->encryption_type = TSK_FS_ENCRYPTION_TYPE_ENUM::TSK_FS_ENCRYPTION_TYPE_NONE;
#endif
}
|