/src/sleuthkit/tsk/pool/apfs_pool.cpp
Line  | Count  | Source  | 
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 "tsk_apfs.hpp"  | 
11  |  |  | 
12  |  | #include "tsk/libtsk.h"  | 
13  |  |  | 
14  |  | #include "tsk/fs/tsk_apfs.hpp"  | 
15  |  | #include "tsk/fs/tsk_fs_i.h"  | 
16  |  | #include "tsk/img/legacy_cache.h"  | 
17  |  |  | 
18  |  | #include <stdexcept>  | 
19  |  |  | 
20  |  | APFSPool::APFSPool(std::vector<img_t>&& imgs, apfs_block_num nx_block_num)  | 
21  | 39  |     : TSKPool(std::forward<std::vector<img_t>>(imgs)),  | 
22  | 39  |       _nx_block_num{nx_block_num} { | 
23  | 39  |   if (_members.size() != 1) { | 
24  | 0  |     throw std::runtime_error(  | 
25  | 0  |         "Only single physical store APFS pools are currently supported");  | 
26  | 0  |   }  | 
27  |  |  | 
28  |  |   // If we're scanning for the latest NXSB then we need to start with the  | 
29  |  |   // last known good NXSB first  | 
30  | 39  |   if (_nx_block_num == APFS_POOL_NX_BLOCK_LATEST) { | 
31  | 39  |     _nx_block_num = APFS_POOL_NX_BLOCK_LAST_KNOWN_GOOD;  | 
32  | 39  |   }  | 
33  |  |  | 
34  | 39  |   std::tie(_img, _offset) = _members[0];  | 
35  |  |  | 
36  | 39  |   auto nxsb = nx(true);  | 
37  |  |  | 
38  |  |   // Update the base members  | 
39  | 39  |   _uuid = nxsb->uuid();  | 
40  | 39  |   _block_size = nxsb->block_size();  | 
41  | 39  |   _dev_block_size = _img->sector_size;  | 
42  | 39  |   _num_blocks = nxsb->num_blocks();  | 
43  |  |  | 
44  |  |   // Check to see if we need to scan for a newer pool  | 
45  | 39  |   if (nx_block_num == APFS_POOL_NX_BLOCK_LATEST) { | 
46  | 34  |     const auto versions = known_versions();  | 
47  |  |  | 
48  | 34  |     if (versions.empty()) { | 
49  | 0  |       _nx_block_num = APFS_POOL_NX_BLOCK_LAST_KNOWN_GOOD;  | 
50  | 0  |       if (tsk_verbose) { | 
51  | 0  |         tsk_fprintf(stderr,  | 
52  | 0  |                     "APFSPool: No checkpoint superblocks found.  Attempting to "  | 
53  | 0  |                     "fall back to last known good superblock\n");  | 
54  | 0  |       }  | 
55  | 34  |     } else { | 
56  | 34  |       const auto highest = std::max_element(  | 
57  | 34  |           versions.begin(), versions.end(),  | 
58  | 435  |           [](const auto& a, const auto& b) { return a.xid < b.xid; }); | 
59  |  |  | 
60  |  |       // No need to do anything, we're already the highest version  | 
61  | 34  |       if (highest->xid != nxsb->xid()) { | 
62  | 8  |         _nx_block_num = highest->nx_block_num;  | 
63  |  |  | 
64  | 8  |         try { | 
65  | 8  |           nxsb = nx(true);  | 
66  | 8  |         } catch (const std::runtime_error&) { | 
67  |  |           // Fallback to last known good block if the latest block is not valid  | 
68  | 0  |           _nx_block_num = APFS_POOL_NX_BLOCK_LAST_KNOWN_GOOD;  | 
69  | 0  |           nxsb = nx(true);  | 
70  | 0  |         }  | 
71  | 8  |       }  | 
72  | 34  |     }  | 
73  | 34  |   }  | 
74  |  |  | 
75  | 39  |   _vol_blocks = nxsb->volume_blocks();  | 
76  | 39  |   _num_vols = static_cast<int>(_vol_blocks.size());  | 
77  |  |  | 
78  |  |   // If the software crypto bit is not set, then either hardware crypto is used  | 
79  |  |   // or there are no volumes that are encrypted.  | 
80  | 39  |   if (bit_is_set(nxsb->sb()->flags, APFS_NXSB_FLAGS_CRYPTO_SW) == false) { | 
81  |  |     // We need to check each volume to determine if any of them have encryption  | 
82  |  |     // enabled.  | 
83  | 3  |     for (const auto& volume : volumes()) { | 
84  | 1  |       if (volume.encrypted()) { | 
85  | 0  |         _hw_crypto = true;  | 
86  | 0  |         break;  | 
87  | 0  |       }  | 
88  | 1  |     }  | 
89  | 3  |   }  | 
90  | 39  | }  | 
91  |  |  | 
92  | 81  | std::unique_ptr<APFSSuperblock> APFSPool::nx(bool validate) const { | 
93  | 81  |   auto nxsb = std::make_unique<APFSSuperblock>((*this), _nx_block_num);  | 
94  |  |  | 
95  | 81  |   if (validate && nxsb->validate_checksum() == false) { | 
96  | 2  |     throw std::runtime_error("NXSB object checksum failed"); | 
97  | 2  |   }  | 
98  |  |  | 
99  | 79  |   return nxsb;  | 
100  | 81  | }  | 
101  |  |  | 
102  | 4  | std::vector<APFSFileSystem> APFSPool::volumes() const { | 
103  | 4  |   std::vector<APFSFileSystem> v{}; | 
104  | 4  |   v.reserve(_vol_blocks.size());  | 
105  |  |  | 
106  | 4  |   for (const auto block : _vol_blocks) { | 
107  | 3  |     v.emplace_back((*this), block);  | 
108  | 3  |   }  | 
109  |  |  | 
110  | 4  |   return v;  | 
111  | 4  | }  | 
112  |  |  | 
113  |  | ssize_t APFSPool::read(uint64_t address, char* buf, size_t buf_size) const  | 
114  | 4.64k  |     noexcept { | 
115  | 4.64k  |   return tsk_img_read(_img, address + _offset, buf, buf_size);  | 
116  | 4.64k  | }  | 
117  |  |  | 
118  | 34  | const std::vector<APFSPool::nx_version> APFSPool::known_versions() const { | 
119  | 34  |   std::vector<nx_version> vers{}; | 
120  |  |  | 
121  | 34  |   const auto nxsb = nx();  | 
122  | 34  |   const auto sb = nxsb->sb();  | 
123  |  |  | 
124  | 34  |   for (auto addr = sb->chkpt_desc_base_addr;  | 
125  | 4.55k  |        addr < sb->chkpt_desc_base_addr + sb->chkpt_desc_block_count; addr++) { | 
126  | 4.51k  |     APFSObject obj{(*this), addr}; | 
127  |  |  | 
128  | 4.51k  |     if (obj.obj_type() != APFS_OBJ_TYPE_SUPERBLOCK ||  | 
129  | 3.44k  |         obj.oid() != nxsb->oid()) { | 
130  | 3.44k  |       continue;  | 
131  | 3.44k  |     }  | 
132  |  |  | 
133  | 1.07k  |     if (!obj.validate_checksum()) { | 
134  | 196  |       continue;  | 
135  | 196  |     }  | 
136  |  |  | 
137  | 882  |     vers.emplace_back(nx_version{addr, obj.xid()}); | 
138  | 882  |   }  | 
139  |  |  | 
140  | 34  |   return vers;  | 
141  | 34  | }  | 
142  |  |  | 
143  | 0  | const std::vector<APFSPool::range> APFSPool::unallocated_ranges() const { | 
144  | 0  |   return nx()->unallocated_ranges();  | 
145  | 0  | }  | 
146  |  |  | 
147  | 0  | void APFSPool::clear_cache() noexcept { | 
148  | 0  |   _block_cache.clear();  | 
149  |  | 
  | 
150  | 0  |   auto cache = static_cast<LegacyCache*>(reinterpret_cast<IMG_INFO*>(_img)->cache);  | 
151  | 0  |   cache->lock();  | 
152  | 0  |   cache->clear();  | 
153  | 0  |   cache->unlock();  | 
154  | 0  | }  |