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