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