1 /*
2 * The Sleuth Kit
3 *
4 * Brian Carrier [carrier <at> sleuthkit [dot] org]
5 * Copyright (c) 2019-2020 Brian Carrier. All Rights reserved
6 * Copyright (c) 2018-2019 BlackBag Technologies. All Rights reserved
7 *
8 * This software is distributed under the Common Public License 1.0
9 */
10 #include "apfs_pool_compat.hpp"
11
12 #include "../fs/apfs_fs.hpp"
13 #include "../fs/tsk_apfs.hpp"
14 #include "../fs/tsk_fs_i.h"
15 #include "../img/pool.hpp"
16
17 #include <stdexcept>
18
~APFSPoolCompat()19 APFSPoolCompat::~APFSPoolCompat() {
20 // Clean up the dynamic allocations
21 if (_info.vol_list != nullptr) {
22 auto vol = _info.vol_list;
23 while (vol != nullptr) {
24 if (vol->desc != nullptr) delete[] vol->desc;
25 if (vol->password_hint != nullptr) delete[] vol->password_hint;
26 vol = vol->next;
27 }
28 delete[] _info.vol_list;
29 _info.vol_list = nullptr;
30 }
31 }
32
init_volumes()33 void APFSPoolCompat::init_volumes() {
34 if (_info.num_vols != 0) {
35 _info.vol_list = new TSK_POOL_VOLUME_INFO[_info.num_vols]();
36
37 int i = 0;
38 TSK_POOL_VOLUME_INFO *last = nullptr;
39
40 for (const auto &volume : volumes()) {
41 auto &vinfo = _info.vol_list[i];
42
43 vinfo.tag = TSK_POOL_VOL_INFO_TAG;
44 vinfo.index = i;
45 vinfo.block = volume.block_num();
46 vinfo.num_blocks = volume.alloc_blocks();
47 vinfo.prev = last;
48 if (vinfo.prev != nullptr) {
49 vinfo.prev->next = &vinfo;
50 }
51
52 vinfo.desc = new char[volume.name().size() + 1];
53 volume.name().copy(vinfo.desc, volume.name().size());
54 vinfo.desc[volume.name().size()] = '\x00';
55
56 if (volume.encrypted()) {
57 vinfo.flags |= TSK_POOL_VOLUME_FLAG_ENCRYPTED;
58
59 vinfo.password_hint = new char[volume.password_hint().size() + 1];
60 volume.password_hint().copy(vinfo.password_hint,
61 volume.password_hint().size());
62 vinfo.password_hint[volume.password_hint().size()] = '\x00';
63 }
64
65 if (volume.case_sensitive()) {
66 vinfo.flags |= TSK_POOL_VOLUME_FLAG_CASE_SENSITIVE;
67 }
68
69 i++;
70 last = &vinfo;
71 }
72 }
73 }
74
poolstat(FILE * hFile) const75 uint8_t APFSPoolCompat::poolstat(FILE *hFile) const noexcept try {
76 tsk_fprintf(hFile, "POOL CONTAINER INFORMATION\n");
77 tsk_fprintf(hFile, "--------------------------------------------\n\n");
78 tsk_fprintf(hFile, "Container %s\n", uuid().str().c_str());
79 tsk_fprintf(hFile, "==============================================\n");
80 tsk_fprintf(hFile, "Type: APFS\n");
81
82 bool has_cdb = false;
83
84 {
85 tsk_fprintf(hFile, "\n");
86 tsk_fprintf(hFile, "NX Block Number: %lld\n", _nx_block_num);
87
88 const auto nxsb = nx();
89
90 tsk_fprintf(hFile, "NX oid: %lld\n", nxsb->oid());
91 tsk_fprintf(hFile, "NX xid: %lld\n", nxsb->xid());
92 tsk_fprintf(hFile, "Checkpoint Descriptor Block: ");
93
94 const auto cdb = nxsb->checkpoint_desc_block();
95
96 if (cdb == 0) {
97 tsk_fprintf(hFile, "Not Found\n");
98 } else {
99 has_cdb = true;
100 tsk_fprintf(hFile, "%lld\n", cdb);
101 }
102
103 tsk_fprintf(hFile, "\n");
104
105 const auto total_space = _info.num_blocks * _info.block_size;
106
107 tsk_fprintf(hFile, "Capacity Ceiling (Size): %llu B\n", total_space);
108
109 if (has_cdb) {
110 const auto free_space = nxsb->num_free_blocks() * _info.block_size;
111 tsk_fprintf(hFile, "Capacity In Use: %llu B\n",
112 total_space - free_space);
113 tsk_fprintf(hFile, "Capacity Available: %llu B\n", free_space);
114 }
115
116 tsk_fprintf(hFile, "\n");
117 tsk_fprintf(hFile, "Block Size: %u B\n", _info.block_size);
118 tsk_fprintf(hFile, "Number of Blocks: %llu\n", _info.num_blocks);
119
120 if (has_cdb) {
121 tsk_fprintf(hFile, "Number of Free Blocks: %llu\n",
122 nxsb->num_free_blocks());
123 }
124 }
125
126 for (const auto &vol : volumes()) {
127 tsk_fprintf(hFile, "|\n");
128 tsk_fprintf(hFile, "+-> Volume %s\n", vol.uuid().str().c_str());
129 tsk_fprintf(hFile, "| ===========================================\n");
130
131 const auto role = [&] {
132 switch (vol.role()) {
133 case APFS_VOLUME_ROLE_NONE:
134 return "No specific role";
135 case APFS_VOLUME_ROLE_SYSTEM:
136 return "System";
137 case APFS_VOLUME_ROLE_USER:
138 return "User";
139 case APFS_VOLUME_ROLE_RECOVERY:
140 return "Recovery";
141 case APFS_VOLUME_ROLE_VM:
142 return "VM";
143 case APFS_VOLUME_ROLE_PREBOOT:
144 return "Preboot";
145 }
146
147 return "Unknown";
148 }();
149 tsk_fprintf(hFile, "| APSB Block Number: %llu\n", vol.block_num());
150 tsk_fprintf(hFile, "| APSB oid: %llu\n", vol.oid());
151 tsk_fprintf(hFile, "| APSB xid: %llu\n", vol.xid());
152 tsk_fprintf(hFile, "| Name (Role): %s (%s)\n", vol.name().c_str(), role);
153
154 tsk_fprintf(hFile, "| Capacity Consumed: %lld B\n", vol.used());
155
156 tsk_fprintf(hFile, "| Capacity Reserved: ");
157 if (vol.reserved() != 0) {
158 tsk_fprintf(hFile, "%lld B\n", vol.reserved());
159 } else {
160 tsk_fprintf(hFile, "None\n");
161 }
162
163 tsk_fprintf(hFile, "| Capacity Quota: ");
164 if (vol.quota() != 0) {
165 tsk_fprintf(hFile, "%lld B\n", vol.quota());
166 } else {
167 tsk_fprintf(hFile, "None\n");
168 }
169 tsk_fprintf(hFile, "| Case Sensitive: %s\n",
170 vol.case_sensitive() ? "Yes" : "No");
171 tsk_fprintf(
172 hFile, "| Encrypted: %s%s\n", vol.encrypted() ? "Yes" : "No",
173 (vol.encrypted() && hardware_crypto()) ? " (hardware assisted)" : "");
174 tsk_fprintf(hFile, "| Formatted by: %s\n", vol.formatted_by().c_str());
175 tsk_fprintf(hFile, "|\n");
176
177 char time_buf[1024];
178 tsk_fprintf(
179 hFile, "| Created: %s\n",
180 tsk_fs_time_to_str_subsecs(vol.created() / 1000000000,
181 vol.created() % 1000000000, time_buf));
182 tsk_fprintf(
183 hFile, "| Changed: %s\n",
184 tsk_fs_time_to_str_subsecs(vol.changed() / 1000000000,
185 vol.changed() % 1000000000, time_buf));
186
187 const auto unmount_log = vol.unmount_log();
188 if (unmount_log.size() != 0) {
189 tsk_fprintf(hFile, "|\n");
190 tsk_fprintf(hFile, "| Unmount Logs\n");
191 tsk_fprintf(hFile, "| ------------\n");
192 tsk_fprintf(hFile,
193 "| Timestamp Log String\n");
194 for (const auto &log : unmount_log) {
195 tsk_fprintf(
196 hFile, "| %s %s\n",
197 tsk_fs_time_to_str_subsecs(log.timestamp / 1000000000,
198 log.timestamp % 1000000000, time_buf),
199 log.logstr.c_str());
200 }
201 }
202
203 if (vol.encrypted() && !hardware_crypto()) {
204 tsk_fprintf(hFile, "|\n");
205 tsk_fprintf(hFile, "| Encryption Info\n");
206 tsk_fprintf(hFile, "| ---------------\n");
207
208 const auto crypto = vol.crypto_info();
209 tsk_fprintf(hFile, "| Password Hint: %s\n",
210 crypto.password_hint.c_str());
211
212 for (const auto &kek : crypto.wrapped_keks) {
213 tsk_fprintf(hFile, "| KEK (%s):", kek.uuid.str().c_str());
214 for (auto i = 0U; i < sizeof(kek.data); i++) {
215 if (i % 8 == 0) {
216 tsk_fprintf(hFile, "\n| ");
217 }
218 tsk_fprintf(hFile, " %2.2X", kek.data[i]);
219 }
220 tsk_fprintf(hFile, "\n|\n");
221
222 tsk_fprintf(hFile, "| Salt:");
223 for (auto i = 0U; i < sizeof(kek.salt); i++) {
224 tsk_fprintf(hFile, " %2.2X", kek.salt[i]);
225 }
226 tsk_fprintf(hFile, "\n| \n");
227
228 tsk_fprintf(hFile, "| Iterations: %lld\n|\n", kek.iterations);
229 }
230
231 tsk_fprintf(hFile, "| Wrapped VEK:");
232 for (auto i = 0U; i < sizeof(crypto.wrapped_vek); i++) {
233 if (i % 8 == 0 && i != 0) {
234 tsk_fprintf(hFile, "\n| ");
235 }
236 tsk_fprintf(hFile, " %2.2X", crypto.wrapped_vek[i]);
237 }
238 tsk_fprintf(hFile, "\n");
239 } else {
240 tsk_fprintf(hFile, "|\n");
241 tsk_fprintf(hFile, "| Root Files\n");
242 tsk_fprintf(hFile, "| -------------\n");
243
244 const auto root = vol.root_jobj_tree();
245 const auto children = root.obj(APFS_ROOT_INODE_NUM).children();
246
247 for (const auto &file : children) {
248 tsk_fprintf(hFile, "| [%8.0llu] %s\n", file.rec.file_id,
249 file.name.c_str());
250 }
251 }
252 }
253
254 if (has_cdb) {
255 tsk_fprintf(hFile, "|\n");
256 tsk_fprintf(hFile, "+-> Unallocated Container Blocks\n");
257 tsk_fprintf(hFile, "| ============================\n");
258 for (const auto &range : nx()->unallocated_ranges()) {
259 tsk_fprintf(hFile, "| 0x%0.8llx-0x%0.8llx\n", range.start_block,
260 range.start_block + range.num_blocks - 1);
261 }
262 }
263
264 return 0;
265 } catch (const std::exception &e) {
266 tsk_error_reset();
267 tsk_error_set_errno(TSK_ERR_POOL_GENPOOL);
268 tsk_error_set_errstr("%s", e.what());
269 return 1;
270 }
271
272 static void
apfs_img_close(TSK_IMG_INFO * img_info)273 apfs_img_close(TSK_IMG_INFO * img_info)
274 {
275 if (img_info == NULL) {
276 return;
277 }
278
279 // Close the pool image
280 tsk_deinit_lock(&(img_info->cache_lock));
281 tsk_img_free(img_info);
282 }
283
284 static void
apfs_img_imgstat(TSK_IMG_INFO * img_info,FILE * file)285 apfs_img_imgstat(TSK_IMG_INFO * img_info, FILE *file)
286 {
287 IMG_POOL_INFO *pool_img_info = (IMG_POOL_INFO *)img_info;
288 const auto pool = static_cast<APFSPoolCompat*>(pool_img_info->pool_info->impl);
289 TSK_IMG_INFO *origInfo = pool->getTSKImgInfo(0);
290 origInfo->imgstat(origInfo, file);
291 }
292
293 static ssize_t
apfs_img_read(TSK_IMG_INFO * img_info,TSK_OFF_T offset,char * buf,size_t len)294 apfs_img_read(TSK_IMG_INFO * img_info, TSK_OFF_T offset, char *buf, size_t len)
295 {
296 IMG_POOL_INFO *pool_img_info = (IMG_POOL_INFO *)img_info;
297 const auto pool = static_cast<APFSPoolCompat*>(pool_img_info->pool_info->impl);
298 TSK_IMG_INFO *origInfo = pool->getTSKImgInfo(0);
299
300 return origInfo->read(origInfo, offset, buf, len);
301 }
302
getImageInfo(const TSK_POOL_INFO * pool_info,TSK_DADDR_T pvol_block)303 TSK_IMG_INFO * APFSPoolCompat::getImageInfo(const TSK_POOL_INFO *pool_info, TSK_DADDR_T pvol_block) noexcept try {
304
305 IMG_POOL_INFO *img_pool_info;
306 TSK_IMG_INFO *img_info;
307
308 if ((img_pool_info =
309 (IMG_POOL_INFO *)tsk_img_malloc(sizeof(IMG_POOL_INFO))) == NULL) {
310 return NULL;
311 }
312
313 img_info = (TSK_IMG_INFO *)img_pool_info;
314
315 img_info->tag = TSK_IMG_INFO_TAG;
316 img_info->itype = TSK_IMG_TYPE_POOL;
317
318 img_pool_info->pool_info = pool_info;
319 img_pool_info->pvol_block = pvol_block;
320
321 img_pool_info->img_info.read = apfs_img_read;
322 img_pool_info->img_info.close = apfs_img_close;
323 img_pool_info->img_info.imgstat = apfs_img_imgstat;
324
325 // Copy original info from the first TSK_IMG_INFO. There was a check in the
326 // APFSPool that _members has only one entry.
327 IMG_POOL_INFO *pool_img_info = (IMG_POOL_INFO *)img_info;
328 const auto pool = static_cast<APFSPoolCompat*>(pool_img_info->pool_info->impl);
329 TSK_IMG_INFO *origInfo = pool->_members[0].first;
330
331 img_info->size = origInfo->size;
332 img_info->num_img = origInfo->num_img;
333 img_info->sector_size = origInfo->sector_size;
334 img_info->page_size = origInfo->page_size;
335 img_info->spare_size = origInfo->spare_size;
336 img_info->images = origInfo->images;
337
338 tsk_init_lock(&(img_info->cache_lock));
339
340 return img_info;
341
342 }
343 catch (const std::exception &e) {
344 tsk_error_reset();
345 tsk_error_set_errno(TSK_ERR_POOL_GENPOOL);
346 tsk_error_set_errstr("%s", e.what());
347 return NULL;
348 }
349
350