Coverage Report

Created: 2025-07-01 06:58

/src/sleuthkit/tsk/fs/apfs.cpp
Line
Count
Source (jump to first uncovered line)
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/util/crypto.hpp"
11
#include "apfs_fs.hpp"
12
#include "tsk_apfs.hpp"
13
14
#include <cstring>
15
16
// MSVC doesn't define ffs/ffsll.
17
#ifdef _MSC_VER
18
#include <intrin.h>
19
20
#ifdef _M_X64  // 64-bit
21
#pragma intrinsic(_BitScanForward64)
22
static __forceinline int lsbset(unsigned __int64 x) {
23
  unsigned long i;
24
25
  if (_BitScanForward64(&i, x)) {
26
    return i + 1;
27
  }
28
29
  return 0;
30
}
31
#else  // 32-bit
32
#pragma intrinsic(_BitScanForward)
33
static __forceinline int lsbset(long x) {
34
  unsigned long i;
35
36
  if (_BitScanForward(&i, x)) {
37
    return i + 1;
38
  }
39
  return 0;
40
}
41
#endif  // _M_X64
42
43
#else  // gcc or clang
44
45
#ifdef __x86_64__
46
0
#define lsbset(x) __builtin_ffsll(x)
47
#else  // 32-bit
48
#define lsbset(x) __builtin_ffs(x)
49
#endif  // __x86_64__
50
51
#endif  // _MSC_VER
52
53
class wrapped_key_parser {
54
  // TODO(JTS): This code assume a well-formed input. It needs some sanity
55
  // checking!
56
57
  using tag = uint8_t;
58
  using view = span<const uint8_t>;
59
60
  const uint8_t* _data;
61
62
0
  size_t get_length(const uint8_t** pos) const noexcept {
63
0
    auto data = *pos;
64
65
0
    size_t len = *data++;
66
67
0
    if (len & 0x80) {
68
0
      len = 0;
69
0
      auto enc_len = len & 0x7F;
70
0
      while (enc_len--) {
71
0
        len <<= 8;
72
0
        len |= *data++;
73
0
      }
74
0
    }
75
76
0
    *pos = data;
77
0
    return len;
78
0
  }
79
80
0
  const view get_tag(tag t) const noexcept {
81
0
    auto data = _data;
82
83
0
    while (true) {
84
0
      const auto tag = *data++;
85
0
      const auto len = get_length(&data);
86
87
0
      if (tag == t) {
88
0
        return {data, len};
89
0
      }
90
91
0
      data += len;
92
0
    }
93
0
  }
94
95
  // Needed for the recursive variadic to compile, but should never be
96
  // called.  TODO(JTS): Use constexpr if when we enforce C++17
97
0
  const view get_data(void) const {
98
0
    throw std::logic_error("this should be unreachable");
99
0
  }
100
101
 public:
102
0
  wrapped_key_parser(const void* data) noexcept : _data{(const uint8_t*)data} {}
103
104
  template <typename... Args>
105
0
  const view get_data(tag t, Args... args) const noexcept {
106
0
    const auto data = get_tag(t);
107
108
0
    if (sizeof...(args) == 0 || !data.valid()) {
109
0
      return data;
110
0
    }
111
112
0
    return wrapped_key_parser{data.data()}.get_data(args...);
113
0
  }
Unexecuted instantiation: span<unsigned char const> const wrapped_key_parser::get_data<int, int>(unsigned char, int, int) const
Unexecuted instantiation: span<unsigned char const> const wrapped_key_parser::get_data<int>(unsigned char, int) const
Unexecuted instantiation: span<unsigned char const> const wrapped_key_parser::get_data<>(unsigned char) const
114
115
  template <typename... Args>
116
0
  uint64_t get_number(tag t, Args... args) const noexcept {
117
0
    const auto data = get_data(t, args...);
118
119
0
    uint64_t n = 0;
120
0
    for (auto p = data.data(); p < data.data() + data.count(); p++) {
121
0
      n <<= 8;
122
0
      n |= *p;
123
0
    }
124
125
0
    return n;
126
0
  }
127
};
128
129
APFSBlock::APFSBlock(const APFSPool& pool, const apfs_block_num block_num)
130
5.62k
    : _storage{}, _pool{pool}, _block_num{block_num} {
131
5.62k
  const auto sz =
132
5.62k
      pool.read(block_num * APFS_BLOCK_SIZE, _storage.data(), APFS_BLOCK_SIZE);
133
5.62k
  if (sz != APFS_BLOCK_SIZE) {
134
23
    throw std::runtime_error("could not read APFSBlock");
135
23
  }
136
5.62k
}
137
138
0
void APFSBlock::decrypt(const uint8_t* key, const uint8_t* key2) noexcept {
139
#ifdef HAVE_LIBCRYPTO
140
    // If the data is encrypted via the T2 chip, we can't decrypt it.  This means
141
    // that if the data wasn't decrypted at acquisition time, then processing will
142
    // likely fail.  Either way, there is no need to decrypt.
143
    if (_pool.hardware_crypto()) {
144
        return;
145
    }
146
147
    aes_xts_decryptor dec{ aes_xts_decryptor::AES_128, key, key2,
148
                          APFS_CRYPTO_SW_BLKSIZE };
149
150
    dec.decrypt_buffer(_storage.data(), _storage.size(),
151
        _block_num * APFS_BLOCK_SIZE);
152
#endif
153
    // TODO: what is the intended behavior here if there is no crypto support.
154
0
}
155
156
0
void APFSBlock::dump() const noexcept {
157
  // Dump contents of block to stdout for debugging
158
0
  for (const auto byte : _storage) {
159
0
    putchar(byte);
160
0
  }
161
0
}
162
163
1.12k
bool APFSObject::validate_checksum() const noexcept {
164
1.12k
  if (obj()->cksum == std::numeric_limits<uint64_t>::max()) {
165
1
    return false;
166
1
  }
167
168
  // Calculate the checksum using the modified fletcher's algorithm
169
1.12k
  const auto checksum = [&]() -> uint64_t {
170
1.12k
    const auto data =
171
1.12k
        reinterpret_cast<const uint32_t*>(_storage.data() + sizeof(uint64_t));
172
1.12k
    const auto len = (_storage.size() - sizeof(uint64_t)) / sizeof(uint32_t);
173
174
1.12k
    constexpr uint64_t mod = std::numeric_limits<uint32_t>::max();
175
176
1.12k
    uint64_t sum1{0};
177
1.12k
    uint64_t sum2{0};
178
179
1.14M
    for (size_t i = 0; i < len; i++) {
180
1.14M
      sum1 = (sum1 + data[i]) % mod;
181
1.14M
      sum2 = (sum2 + sum1) % mod;
182
1.14M
    }
183
184
1.12k
    const auto ck_low = mod - ((sum1 + sum2) % mod);
185
1.12k
    const auto ck_high = mod - ((sum1 + ck_low) % mod);
186
187
1.12k
    return (ck_high << 32) | ck_low;
188
1.12k
  }();
189
190
  // Compare calculated checksum with the value in the object header
191
1.12k
  return (checksum == obj()->cksum);
192
1.12k
}
193
194
APFSSuperblock::APFSSuperblock(const APFSPool& pool,
195
                               const apfs_block_num block_num)
196
93
    : APFSObject(pool, block_num), _spaceman{} {
197
93
  if (obj_type() != APFS_OBJ_TYPE_SUPERBLOCK) {
198
8
    throw std::runtime_error("APFSSuperblock: invalid object type");
199
8
  }
200
201
85
  if (sb()->magic != APFS_NXSUPERBLOCK_MAGIC) {
202
0
    throw std::runtime_error("APFSSuperblock: invalid magic");
203
0
  }
204
205
85
  if (bit_is_set(sb()->incompatible_features, APFS_NXSB_INCOMPAT_VERSION1)) {
206
0
    throw std::runtime_error(
207
0
        "APFSSuperblock: Pre-release versions of APFS are not supported");
208
0
  }
209
210
85
  if (bit_is_set(sb()->incompatible_features, APFS_NXSB_INCOMPAT_FUSION)) {
211
2
    if (tsk_verbose) {
212
0
      tsk_fprintf(stderr,
213
0
                  "WARNING: APFS fusion drives may not be fully supported\n");
214
0
    }
215
2
  }
216
217
85
  if (block_size() != APFS_BLOCK_SIZE) {
218
0
    throw std::runtime_error(
219
0
        "APFSSuperblock: invalid or unsupported block size");
220
0
  }
221
85
}
222
223
16
const std::vector<apfs_block_num> APFSSuperblock::volume_blocks() const {
224
16
  std::vector<apfs_block_num> vec{};
225
226
16
  const auto root = omap().root<APFSObjectBtreeNode>();
227
228
16
  for (const auto& e : root.entries()) {
229
2
    vec.emplace_back(e.value->paddr);
230
2
  }
231
232
16
  return vec;
233
16
}
234
235
0
const std::vector<apfs_block_num> APFSSuperblock::sm_bitmap_blocks() const {
236
0
  const auto entries = spaceman().bm_entries();
237
238
0
  std::vector<apfs_block_num> v{};
239
0
  v.reserve(entries.size());
240
241
0
  for (const auto& entry : entries) {
242
0
    if (entry.bm_block != 0) {
243
0
      v.emplace_back(entry.bm_block);
244
0
    }
245
0
  }
246
247
0
  return v;
248
0
}
249
250
0
const std::vector<uint64_t> APFSSuperblock::volume_oids() const {
251
0
  std::vector<uint64_t> v{};
252
253
0
  for (auto i = 0U; i < sb()->max_fs_count; i++) {
254
0
    const auto oid = sb()->fs_oids[i];
255
256
0
    if (oid == 0) {
257
0
      break;
258
0
    }
259
260
0
    v.emplace_back(oid);
261
0
  }
262
263
0
  return v;
264
0
}
265
266
0
apfs_block_num APFSSuperblock::checkpoint_desc_block() const {
267
0
  for (auto i = 0U; i < sb()->chkpt_desc_block_count; i++) {
268
0
    const auto block_num = sb()->chkpt_desc_base_addr + i;
269
0
    const auto block = APFSObject(_pool, block_num);
270
271
0
    if (!block.validate_checksum()) {
272
0
      if (tsk_verbose) {
273
0
        tsk_fprintf(stderr,
274
0
                    "APFSSuperblock::checkpoint_desc_block: Block %lld did not "
275
0
                    "validate.\n",
276
0
                    block_num);
277
0
      }
278
0
      continue;
279
0
    }
280
281
0
    if (block.xid() == xid() &&
282
0
        block.obj_type() == APFS_OBJ_TYPE_CHECKPOINT_DESC) {
283
0
      return block_num;
284
0
    }
285
0
  }
286
287
  // We didn't find anything so return 0;
288
0
  return 0;
289
0
}
290
291
0
const APFSSpaceman& APFSSuperblock::spaceman() const {
292
0
  if (_spaceman != nullptr) {
293
0
    return *_spaceman;
294
0
  }
295
296
0
#ifdef TSK_MULTITHREAD_LIB
297
  // Since this function is const, and const methods generally are assumed to be
298
  // thread safe, we ideally want to it be thread safe so multiple threads
299
  // aren't trying to initialize at the same time.
300
0
  std::lock_guard<std::mutex> lock{_spaceman_init_lock};
301
302
  // Check again to make sure someone else didn't already beat us to this.
303
0
  if (_spaceman != nullptr) {
304
0
    return *_spaceman;
305
0
  }
306
0
#endif
307
308
0
  const APFSCheckpointMap cd{_pool, checkpoint_desc_block()};
309
310
0
  _spaceman = std::make_unique<APFSSpaceman>(
311
0
      _pool, cd.get_object_block(sb()->spaceman_oid, APFS_OBJ_TYPE_SPACEMAN));
312
313
0
  return *_spaceman;
314
0
}
315
316
0
APFSSuperblock::Keybag APFSSuperblock::keybag() const {
317
0
  if (sb()->keylocker.start_paddr == 0) {
318
0
    throw std::runtime_error("no keybag found");
319
0
  }
320
321
0
  return {(*this)};
322
0
}
323
324
APFSOmap::APFSOmap(const APFSPool& pool, const apfs_block_num block_num)
325
17
    : APFSObject(pool, block_num) {
326
17
  if (obj_type() != APFS_OBJ_TYPE_OMAP) {
327
7
    throw std::runtime_error("APFSOmap: invalid object type");
328
7
  }
329
17
}
330
331
APFSFileSystem::APFSFileSystem(const APFSPool& pool,
332
                               const apfs_block_num block_num)
333
6
    : APFSObject(pool, block_num) {
334
6
  if (obj_type() != APFS_OBJ_TYPE_FS) {
335
1
    throw std::runtime_error("APFSFileSystem: invalid object type");
336
1
  }
337
338
5
  if (fs()->magic != APFS_FS_MAGIC) {
339
0
    throw std::runtime_error("APFSFileSystem: invalid magic");
340
0
  }
341
342
5
  if (encrypted() && pool.hardware_crypto() == false) {
343
0
    init_crypto_info();
344
0
  }
345
5
}
346
347
APFSFileSystem::wrapped_kek::wrapped_kek(TSKGuid&& id,
348
                                         const std::unique_ptr<uint8_t[]>& kp)
349
0
    : uuid{std::forward<TSKGuid>(id)} {
350
  // Parse KEK
351
0
  wrapped_key_parser wp{kp.get()};
352
353
  // Get flags
354
0
  flags = wp.get_number(0x30, 0xA3, 0x82);
355
356
  // Get wrapped KEK
357
0
  auto kek_data = wp.get_data(0x30, 0xA3, 0x83);
358
0
  if (kek_data.count() != sizeof(data)) {
359
0
    throw std::runtime_error("invalid KEK size");
360
0
  }
361
0
  std::memcpy(data, kek_data.data(), sizeof(data));
362
363
  // Get iterations
364
0
  iterations = wp.get_number(0x30, 0xA3, 0x84);
365
366
  // Get salt
367
0
  kek_data = wp.get_data(0x30, 0xA3, 0x85);
368
0
  if (kek_data.count() != sizeof(salt)) {
369
0
    throw std::runtime_error("invalid salt size");
370
0
  }
371
0
  std::memcpy(salt, kek_data.data(), sizeof(salt));
372
0
}
373
374
APFSFileSystem::APFSFileSystem(const APFSPool& pool,
375
                               const apfs_block_num block_num,
376
                               const std::string& password)
377
2
    : APFSFileSystem(pool, block_num) {
378
2
  if (encrypted()) {
379
0
    unlock(password);
380
0
  }
381
2
}
382
383
// These are the known special recovery UUIDs.  The ones that are commented out
384
// are currently supported.
385
static const auto unsupported_recovery_keys = {
386
    TSKGuid{"c064ebc6-0000-11aa-aa11-00306543ecac"},  // Institutional Recovery
387
    TSKGuid{"2fa31400-baff-4de7-ae2a-c3aa6e1fd340"},  // Institutional User
388
    // TSKGuid{"ebc6C064-0000-11aa-aa11-00306543ecac"},  // Personal Recovery
389
    TSKGuid{"64c0c6eb-0000-11aa-aa11-00306543ecac"},  // iCould Recovery
390
    TSKGuid{"ec1c2ad9-b618-4ed6-bd8d-50f361c27507"},  // iCloud User
391
};
392
393
0
void APFSFileSystem::init_crypto_info() {
394
0
    try {
395
396
        // Get container keybag
397
0
        const auto container_kb = _pool.nx()->keybag();
398
399
0
        auto data = container_kb.get_key(uuid(), APFS_KB_TYPE_VOLUME_KEY);
400
0
        if (data == nullptr) {
401
0
            throw std::runtime_error(
402
0
                "APFSFileSystem: can not find volume encryption key");
403
0
        }
404
405
0
        wrapped_key_parser wp{ data.get() };
406
407
        // Get Wrapped VEK
408
0
        auto kek_data = wp.get_data(0x30, 0xA3, 0x83);
409
0
        if (kek_data.count() != sizeof(_crypto.wrapped_vek)) {
410
0
            throw std::runtime_error("invalid VEK size");
411
0
        }
412
0
        std::memcpy(_crypto.wrapped_vek, kek_data.data(),
413
0
            sizeof(_crypto.wrapped_vek));
414
415
        // Get VEK Flags
416
0
        _crypto.vek_flags = wp.get_number(0x30, 0xA3, 0x82);
417
418
        // Get VEK UUID
419
0
        kek_data = wp.get_data(0x30, 0xA3, 0x81);
420
0
        if (kek_data.count() != sizeof(_crypto.vek_uuid)) {
421
0
            throw std::runtime_error("invalid UUID size");
422
0
        }
423
0
        std::memcpy(_crypto.vek_uuid, kek_data.data(), sizeof(_crypto.vek_uuid));
424
425
0
        data = container_kb.get_key(uuid(), APFS_KB_TYPE_UNLOCK_RECORDS);
426
0
        if (data == nullptr) {
427
0
            throw std::runtime_error(
428
0
                "APFSFileSystem: can not find volume recovery key");
429
0
        }
430
431
0
        const auto rec =
432
0
            reinterpret_cast<const apfs_volrec_keybag_value*>(data.get());
433
434
0
        if (rec->num_blocks != 1) {
435
0
            throw std::runtime_error(
436
0
                "only single block keybags are currently supported");
437
0
        }
438
439
0
        _crypto.recs_block_num = rec->start_block;
440
441
0
        Keybag recs{ (*this), _crypto.recs_block_num };
442
443
0
        data = recs.get_key(uuid(), APFS_KB_TYPE_PASSPHRASE_HINT);
444
445
0
        if (data != nullptr) {
446
0
            _crypto.password_hint = std::string((const char*)data.get());
447
0
        }
448
449
        // Get KEKs
450
0
        auto keks = recs.get_keys();
451
0
        if (keks.empty()) {
452
0
            throw std::runtime_error("could not find any KEKs");
453
0
        }
454
455
0
        for (auto& k : keks) {
456
0
            if (k.type != APFS_KB_TYPE_UNLOCK_RECORDS) {
457
0
                continue;
458
0
            }
459
460
0
            if (std::find(unsupported_recovery_keys.begin(),
461
0
                unsupported_recovery_keys.end(),
462
0
                k.uuid) != unsupported_recovery_keys.end()) {
463
                // Skip unparsable recovery KEKs
464
0
                if (tsk_verbose) {
465
0
                    tsk_fprintf(stderr, "apfs: skipping unsupported KEK type: %s\n",
466
0
                        k.uuid.str().c_str());
467
0
                }
468
0
                continue;
469
0
            }
470
471
0
            _crypto.wrapped_keks.emplace_back(wrapped_kek{ std::move(k.uuid), k.data });
472
0
        }
473
0
    }
474
0
    catch (std::exception& e) {
475
0
        if (tsk_verbose) {
476
0
            tsk_fprintf(stderr, "APFSFileSystem::init_crypto_info: %s", e.what());
477
0
        }
478
0
    }
479
0
}
480
481
0
bool APFSFileSystem::unlock(const std::string& password) noexcept {
482
#ifdef HAVE_LIBCRYPTO
483
  if (_crypto.unlocked) {
484
    // Already unlocked
485
    return true;
486
  }
487
488
  // TODO(JTS): If bits 32:16 are set to 1, some other sort of KEK decryption is
489
  // used (see _fv_decrypt_vek in AppleKeyStore).
490
  if (_crypto.unk16()) {
491
    if (tsk_verbose) {
492
      tsk_fprintf(stderr,
493
                  "apfs: UNK16 is set in VEK.  Decryption will likely fail.\n");
494
    }
495
  }
496
497
  // Check the password against all possible KEKs
498
  for (const auto& wk : _crypto.wrapped_keks) {
499
    // If the 57th bit of the KEK flags is set, then the kek is a CoreStorage
500
    // KEK
501
    const auto kek_len = (wk.cs()) ? 0x10 : 0x20;
502
503
    // TODO(JTS): If the 56th bit of the KEK flags is set, some sort of hardware
504
    // decryption is needed
505
    if (wk.hw_crypt()) {
506
      if (tsk_verbose) {
507
        tsk_fprintf(
508
            stderr,
509
            "apfs: hardware decryption is not yet supported. KEK decryption "
510
            "will likely fail\n");
511
      }
512
    }
513
514
    const auto user_key = pbkdf2_hmac_sha256(password, wk.salt, sizeof(wk.salt),
515
                                             wk.iterations, kek_len);
516
    if (user_key == nullptr) {
517
      if (tsk_verbose) {
518
        tsk_fprintf(stderr, "apfs: can not generate user key\n");
519
      }
520
      continue;
521
    }
522
523
    const auto kek =
524
        rfc3394_key_unwrap(user_key.get(), kek_len, wk.data, kek_len + 8);
525
    if (kek == nullptr) {
526
      if (tsk_verbose) {
527
        tsk_fprintf(stderr,
528
                    "apfs: KEK %s can not be unwrapped with given password\n",
529
                    wk.uuid.str().c_str());
530
      }
531
      continue;
532
    }
533
534
    // If the 57th bit of the VEK flags is set, then the VEK is a
535
    // CoreStorage VEK
536
    const auto vek_len = (_crypto.cs()) ? 0x10 : 0x20;
537
538
    // If a 128 bit VEK is wrapped with a 256 bit KEK then only the first 128
539
    // bits of the KEK are used.
540
    const auto vek = rfc3394_key_unwrap(kek.get(), std::min(kek_len, vek_len),
541
                                        _crypto.wrapped_vek, vek_len + 8);
542
    if (vek == nullptr) {
543
      if (tsk_verbose) {
544
        tsk_fprintf(stderr, "apfs: failed to unwrap VEK\n");
545
      }
546
      continue;
547
    }
548
549
    _crypto.password = password;
550
    std::memcpy(_crypto.vek, vek.get(), vek_len);
551
552
    if (_crypto.cs()) {
553
      // For volumes that were converted from CoreStorage, the tweak is the
554
      // first 128-bits of SHA256(vek + vekuuid)
555
      std::memcpy(_crypto.vek + 0x10, _crypto.vek_uuid,
556
                  sizeof(_crypto.vek_uuid));
557
558
      const auto hash = hash_buffer_sha256(_crypto.vek, sizeof(_crypto.vek));
559
560
      std::memcpy(_crypto.vek + 0x10, hash.get(), 0x10);
561
    }
562
563
    _crypto.unlocked = true;
564
565
    return true;
566
  }
567
568
  return false;
569
#else
570
0
    if (tsk_verbose) {
571
0
        tsk_fprintf(stderr, "apfs: crypto library not loaded\n");
572
0
    }
573
0
    return false;
574
0
#endif
575
0
}
576
577
const std::vector<APFSFileSystem::unmount_log_t> APFSFileSystem::unmount_log()
578
0
    const {
579
0
  std::vector<unmount_log_t> v{};
580
581
0
  for (auto i = 0; i < 8; i++) {
582
0
    const auto& log = fs()->unmount_logs[i];
583
584
0
    if (log.timestamp == 0) {
585
0
      return v;
586
0
    }
587
588
0
    v.emplace_back(
589
0
        unmount_log_t{log.timestamp, log.kext_ver_str, log.last_xid});
590
0
  }
591
592
0
  return v;
593
0
}
594
595
const std::vector<APFSFileSystem::snapshot_t> APFSFileSystem::snapshots()
596
0
    const {
597
0
  std::vector<snapshot_t> v{};
598
599
0
  const APFSSnapshotMetaBtreeNode snap_tree{_pool, fs()->snap_meta_tree_oid};
600
601
0
  struct key_type {
602
0
    uint64_t xid_and_type;
603
604
0
    inline uint64_t snap_xid() const noexcept {
605
0
      return bitfield_value(xid_and_type, 60, 0);
606
0
    }
607
608
0
    inline uint64_t type() const noexcept {
609
0
      return bitfield_value(xid_and_type, 4, 60);
610
0
    }
611
0
  };
612
613
0
  using value_type = apfs_snap_metadata;
614
615
0
  std::for_each(snap_tree.begin(), snap_tree.end(), [&](const auto& entry) {
616
0
    const auto key = entry.key.template as<key_type>();
617
0
    const auto value = entry.value.template as<value_type>();
618
619
0
    if (key->type() != APFS_JOBJTYPE_SNAP_METADATA) {
620
0
      return;
621
0
    }
622
623
0
    v.emplace_back(snapshot_t{
624
0
        {value->name, value->name_length - 1U},  // name
625
0
        value->create_time,                      // timestamp
626
0
        key->snap_xid(),                         // snap_xid
627
0
        (value->extentref_tree_oid == 0),        // dataless
628
0
    });
629
0
  });
630
631
0
  return v;
632
0
}
633
634
0
APFSJObjTree APFSFileSystem::root_jobj_tree() const {
635
0
  return {_pool, omap_root(), rdo(), crypto_info()};
636
0
}
637
638
0
apfs_block_num APFSFileSystem::omap_root() const {
639
0
  return APFSOmap{_pool, fs()->omap_oid}.root_block();
640
0
}
641
642
APFSJObjBtreeNode::APFSJObjBtreeNode(const APFSObjectBtreeNode* obj_root,
643
                                     apfs_block_num block_num,
644
                                     const uint8_t* key)
645
#ifdef HAVE_LIBCRYPTO
646
    : APFSBtreeNode(obj_root->pool(), block_num, key), _obj_root{obj_root} {
647
#else
648
2
    : APFSBtreeNode(obj_root->pool(), block_num, nullptr), _obj_root{ obj_root } {
649
2
#endif
650
2
  if (subtype() != APFS_OBJ_TYPE_FSTREE) {
651
0
    throw std::runtime_error("APFSJObjBtreeNode: invalid subtype");
652
0
  }
653
2
}
654
655
656
1
APFSObjectBtreeNode::iterator APFSObjectBtreeNode::find(uint64_t oid) const {
657
1
  return APFSBtreeNode::find(
658
1
      oid, [xid = this->_xid](const auto& key,
659
1
                              const auto oid) noexcept->int64_t {
660
1
        if ((key->oid == oid) && (key->xid > xid)) {
661
0
          return key->xid - xid;
662
0
        }
663
664
1
        return (key->oid - oid);
665
1
      });
666
1
}
667
668
APFSObjectBtreeNode::APFSObjectBtreeNode(const APFSPool& pool,
669
                                         apfs_block_num block_num)
670
10
    : APFSBtreeNode(pool, block_num), _xid{xid()} {
671
10
  if (subtype() != APFS_OBJ_TYPE_OMAP) {
672
0
    throw std::runtime_error("APFSObjectBtreeNode: invalid subtype");
673
0
  }
674
10
}
675
676
APFSObjectBtreeNode::APFSObjectBtreeNode(const APFSPool& pool,
677
                                         apfs_block_num block_num,
678
                                         uint64_t snap_xid)
679
0
    : APFSBtreeNode(pool, block_num), _xid{snap_xid} {
680
0
  if (subtype() != APFS_OBJ_TYPE_OMAP) {
681
0
    throw std::runtime_error("APFSObjectBtreeNode: invalid subtype");
682
0
  }
683
0
}
684
685
APFSSnapshotMetaBtreeNode::APFSSnapshotMetaBtreeNode(const APFSPool& pool,
686
                                                     apfs_block_num block_num)
687
0
    : APFSBtreeNode(pool, block_num) {
688
0
  if (subtype() != APFS_OBJ_TYPE_SNAPMETATREE) {
689
0
    throw std::runtime_error("APFSSnapshotMetaBtreeNode: invalid subtype");
690
0
  }
691
0
}
692
693
APFSExtentRefBtreeNode::APFSExtentRefBtreeNode(const APFSPool& pool,
694
                                               apfs_block_num block_num)
695
0
    : APFSBtreeNode(pool, block_num) {
696
0
  if (subtype() != APFS_OBJ_TYPE_BLOCKREFTREE) {
697
0
    throw std::runtime_error("APFSExtentRefBtreeNode: invalid subtype");
698
0
  }
699
0
}
700
701
APFSCheckpointMap::APFSCheckpointMap(const APFSPool& pool,
702
                                     const apfs_block_num block_num)
703
0
    : APFSObject(pool, block_num) {
704
0
  if (obj_type() != APFS_OBJ_TYPE_CHECKPOINT_DESC) {
705
0
    throw std::runtime_error("APFSCheckpointMap: invalid object type");
706
0
  }
707
0
}
708
709
apfs_block_num APFSCheckpointMap::get_object_block(
710
0
    uint64_t oid, APFS_OBJ_TYPE_ENUM type) const {
711
0
  const auto entries = map()->entries;
712
713
0
  for (auto i = 0U; i < map()->count; i++) {
714
0
    const auto& entry = entries[i];
715
716
0
    if (entry.oid == oid && entry.type == type) {
717
0
      return entry.paddr;
718
0
    }
719
0
  }
720
721
  // Not found
722
0
  throw std::runtime_error(
723
0
      "APFSCheckpointMap::get_object_block: object not found");
724
0
}
725
726
APFSSpaceman::APFSSpaceman(const APFSPool& pool, const apfs_block_num block_num)
727
0
    : APFSObject(pool, block_num), _bm_entries{} {
728
0
  if (obj_type() != APFS_OBJ_TYPE_SPACEMAN) {
729
0
    throw std::runtime_error("APFSSpaceman: invalid object type");
730
0
  }
731
0
}
732
733
0
const std::vector<APFSSpacemanCIB::bm_entry>& APFSSpaceman::bm_entries() const {
734
0
  if (!_bm_entries.empty()) {
735
0
    return _bm_entries;
736
0
  }
737
738
0
#ifdef TSK_MULTITHREAD_LIB
739
  // Since this function is const, and const methods generally are assumed to be
740
  // thread safe, we ideally want to it be thread safe so multiple threads
741
  // aren't trying to initialize at the same time.
742
0
  std::lock_guard<std::mutex> lock{_bm_entries_init_lock};
743
744
  // Check again to make sure someone else didn't already beat us to this.
745
0
  if (!_bm_entries.empty()) {
746
0
    return _bm_entries;
747
0
  }
748
749
  // Our above checks would not prevent someone from accessing the member while
750
  // the initialization is in progress, so let's initialize a temporary and them
751
  // move it into the member instead.
752
0
  decltype(_bm_entries) bm_entries{};
753
#else
754
  // There's no possibility for contention, so let's just initialize the member
755
  // directly so that we can save the move.
756
  auto& bm_entries = _bm_entries;
757
#endif
758
759
0
  bm_entries.reserve(sm()->devs[APFS_SD_MAIN].cib_count);
760
761
0
  const auto cib_blocks = [&] {
762
0
    std::vector<apfs_block_num> v{};
763
0
    v.reserve(sm()->devs[APFS_SD_MAIN].cib_count);
764
765
0
    const auto entries = this->entries();
766
767
    // Is the next level cib?
768
0
    if (sm()->devs[APFS_SD_MAIN].cab_count == 0) {
769
      // Our entires contain the cib blocks
770
0
      for (auto i = 0U; i < sm()->devs[APFS_SD_MAIN].cib_count; i++) {
771
0
        v.emplace_back(entries[i]);
772
0
      }
773
774
0
      return v;
775
0
    }
776
777
    // The next level is cab, not cib so we need to recurse them
778
0
    for (auto i = 0U; i < sm()->devs[APFS_SD_MAIN].cab_count; i++) {
779
0
      const APFSSpacemanCAB cab(_pool, entries[i]);
780
0
      const auto cab_entries = cab.cib_blocks();
781
782
      // Append the blocks to the vector
783
0
      std::copy(cab_entries.begin(), cab_entries.end(), std::back_inserter(v));
784
0
    }
785
786
0
    return v;
787
0
  }();
788
789
0
  for (const auto block : cib_blocks) {
790
0
    const APFSSpacemanCIB cib(_pool, block);
791
792
0
    const auto entries = cib.bm_entries();
793
794
    // Append the entries to the vector
795
0
    std::copy(entries.begin(), entries.end(), std::back_inserter(bm_entries));
796
0
  }
797
798
  // Sort the entries by offset
799
0
  std::sort(bm_entries.begin(), bm_entries.end(),
800
0
            [](const auto& a, const auto& b) { return (a.offset < b.offset); });
801
802
0
#ifdef TSK_MULTITHREAD_LIB
803
  // Now that we're fully initialized we can now move our initialized vector
804
  // into the member to signal that we're ready for access.
805
0
  _bm_entries = std::move(bm_entries);
806
0
#endif
807
808
0
  return _bm_entries;
809
0
}
810
811
const std::vector<APFSSpaceman::range> APFSSpaceman::unallocated_ranges()
812
0
    const {
813
0
  std::vector<range> v{};
814
815
0
  for (const auto& entry : bm_entries()) {
816
0
    if (entry.free_blocks == 0) {
817
      // No free ranges to add
818
0
      continue;
819
0
    }
820
821
0
    if (entry.total_blocks == entry.free_blocks) {
822
      // The entire bitmap block is free
823
0
      if (!v.empty() &&
824
0
          v.back().start_block + v.back().num_blocks == entry.offset) {
825
        // We're within the same range as the last one, so just update the
826
        // count
827
0
        v.back().num_blocks += entry.free_blocks;
828
0
      } else {
829
        // We're not contiguous with the last range, so add a new one
830
0
        v.emplace_back(range{entry.offset, entry.free_blocks});
831
0
      }
832
0
      continue;
833
0
    }
834
835
    // We've got to enumerate the bitmap block for it's ranges
836
0
    const auto ranges = APFSBitmapBlock{_pool, entry}.unallocated_ranges();
837
838
    // TODO(JTS): We could possibly de-duplicate the first range if it's
839
    // contiguous with the last range, but the overhead might outweigh the
840
    // convenience
841
0
    std::copy(ranges.begin(), ranges.end(), std::back_inserter(v));
842
0
  }
843
844
0
  return v;
845
0
}
846
847
APFSSpacemanCIB::APFSSpacemanCIB(const APFSPool& pool,
848
                                 const apfs_block_num block_num)
849
0
    : APFSObject(pool, block_num) {
850
0
  if (obj_type() != APFS_OBJ_TYPE_SPACEMAN_CIB) {
851
0
    throw std::runtime_error("APFSSpacemanCIB: invalid object type");
852
0
  }
853
0
}
854
855
const std::vector<APFSSpacemanCIB::bm_entry> APFSSpacemanCIB::bm_entries()
856
0
    const {
857
0
  std::vector<bm_entry> v{};
858
0
  v.reserve(cib()->entry_count);
859
860
0
  const auto entries = cib()->entries;
861
0
  for (auto i = 0U; i < cib()->entry_count; i++) {
862
0
    const auto& entry = entries[i];
863
0
    v.emplace_back(bm_entry{entry.addr, entry.block_count, entry.free_count,
864
0
                            entry.bm_addr});
865
0
  }
866
867
0
  return v;
868
0
}
869
870
APFSSpacemanCAB::APFSSpacemanCAB(const APFSPool& pool,
871
                                 const apfs_block_num block_num)
872
0
    : APFSObject(pool, block_num) {
873
0
  if (obj_type() != APFS_OBJ_TYPE_SPACEMAN_CAB) {
874
0
    throw std::runtime_error("APFSSpacemanCAB: invalid object type");
875
0
  }
876
0
}
877
878
0
const std::vector<apfs_block_num> APFSSpacemanCAB::cib_blocks() const {
879
0
  std::vector<apfs_block_num> v{};
880
0
  v.reserve(cib_count());
881
882
0
  const auto entries = cab()->cib_blocks;
883
884
0
  for (auto i = 0U; i < cib_count(); i++) {
885
0
    v.emplace_back(entries[i]);
886
0
  }
887
888
0
  return v;
889
0
}
890
891
APFSBitmapBlock::APFSBitmapBlock(const APFSPool& pool,
892
                                 const APFSSpacemanCIB::bm_entry& entry)
893
0
    : APFSBlock(pool, entry.bm_block), _entry{entry} {}
894
895
0
uint32_t APFSBitmapBlock::next() noexcept {
896
0
  while (!done()) {
897
    // Calculate the index of the bit to be evaluated.
898
0
    const auto i = _hint % cached_bits;
899
900
    // If we're evaluating the first bit then we need to cache the next set
901
    // from the array.
902
0
    if (i == 0) {
903
0
      cache_next();
904
905
      // If there are no set bits then there's nothing to scan for, so let's
906
      // try again with the next set of bits.
907
0
      if (_cache == 0) {
908
0
        _hint += cached_bits;
909
0
        continue;
910
0
      }
911
0
    }
912
913
    // Mask the fetched value and count the number of trailing zero bits.
914
0
    const auto c = lsbset((_cache >> i) << i);
915
916
    // If c is non-zero then there are set bits.
917
0
    if (c != 0) {
918
      // There are set bits.  We just need to make sure that they're within
919
      // the range we're scanning for.
920
921
      // Adjust the hint for the next call
922
0
      _hint += c - i;
923
924
      // Check to see if we're still in range
925
0
      if (_hint - 1 < _entry.total_blocks) {
926
0
        return _hint - 1;
927
0
      }
928
929
      // The hit is outside of our scanned range
930
0
      return no_bits_left;
931
0
    }
932
933
    // There are no set bits, so we need to adjust the hint to the next set of
934
    // bits and try again.
935
0
    _hint += cached_bits - i;
936
0
  }
937
938
0
  return no_bits_left;
939
0
}
940
941
0
const std::vector<APFSSpaceman::range> APFSBitmapBlock::unallocated_ranges() {
942
  // Check for special case where all blocks are allocated
943
0
  if (_entry.free_blocks == 0) {
944
0
    return {};
945
0
  }
946
947
  // Check for special cases where all blocks are free
948
0
  if (_entry.free_blocks == _entry.total_blocks) {
949
0
    return {{_entry.offset, _entry.total_blocks}};
950
0
  }
951
952
0
  reset();
953
0
  _mode = mode::unset;
954
955
0
  std::vector<APFSSpaceman::range> v{};
956
957
0
  while (!done()) {
958
    // Get the start of the range.
959
0
    const auto s = next();
960
961
    // If there's no start then we're done.
962
0
    if (s == no_bits_left) {
963
0
      break;
964
0
    }
965
966
    // Toggle the scan mode to look for the next type of bit.
967
0
    toggle_mode();
968
969
    // Get the end of the range.
970
0
    auto e = next();
971
972
    // If there's no end then we set the end of the range to the end of the
973
    // bitmap.
974
0
    if (e == no_bits_left) {
975
0
      e = _entry.total_blocks;
976
0
    }
977
978
    // Add the range description to the vector.
979
0
    v.emplace_back(APFSSpaceman::range{s + _entry.offset, e - s});
980
981
    // Toggle the scan mode for the next scan.
982
0
    toggle_mode();
983
0
  }
984
985
0
  return v;
986
0
}
987
988
APFSKeybag::APFSKeybag(const APFSPool& pool, const apfs_block_num block_num,
989
                       const uint8_t* key, const uint8_t* key2)
990
0
    : APFSObject(pool, block_num) {
991
0
  decrypt(key, key2);
992
993
0
  if (!validate_checksum()) {
994
0
    throw std::runtime_error("keybag did not decrypt properly");
995
0
  }
996
997
0
  if (kb()->version != 2) {
998
0
    throw std::runtime_error("keybag version not supported");
999
0
  }
1000
0
}
1001
1002
std::unique_ptr<uint8_t[]> APFSKeybag::get_key(const TSKGuid& uuid,
1003
0
                                               uint16_t type) const {
1004
0
  if (kb()->num_entries == 0) {
1005
0
    return nullptr;
1006
0
  }
1007
1008
  // First key is immediately after the header
1009
0
  auto next_key = kb()->first_key;
1010
1011
0
  for (auto i = 0U; i < kb()->num_entries; i++) {
1012
0
    if (next_key->type == type &&
1013
0
        std::memcmp(next_key->uuid, uuid.bytes().data(), 16) == 0) {
1014
      // We've found a matching key.  Copy it's data to a pointer and return it.
1015
0
      const auto data = reinterpret_cast<const uint8_t*>(next_key + 1);
1016
1017
      // We're padding the data with an extra byte so we can null-terminate
1018
      // any data strings.  There might be a better way.
1019
0
      auto dp = std::make_unique<uint8_t[]>(next_key->length + 1);
1020
1021
0
      std::memcpy(dp.get(), data, next_key->length);
1022
1023
0
      return dp;
1024
0
    }
1025
1026
    // Calculate address of next key (ensuring alignment)
1027
1028
0
    const auto nk_addr =
1029
0
        (uintptr_t)next_key +
1030
0
        ((sizeof(*next_key) + next_key->length + 0x0F) & ~0x0FULL);
1031
1032
0
    next_key = reinterpret_cast<const apfs_keybag_key*>(nk_addr);
1033
0
  }
1034
1035
  // Not Found
1036
0
  return nullptr;
1037
0
}
1038
1039
0
std::vector<APFSKeybag::key> APFSKeybag::get_keys() const {
1040
0
  std::vector<key> keys;
1041
1042
  // First key is immediately after the header
1043
0
  auto next_key = kb()->first_key;
1044
1045
0
  for (auto i = 0U; i < kb()->num_entries; i++) {
1046
0
    const auto data = reinterpret_cast<const uint8_t*>(next_key + 1);
1047
1048
    // We're padding the data with an extra byte so we can null-terminate
1049
    // any data strings.  There might be a better way.
1050
0
    auto dp = std::make_unique<uint8_t[]>(next_key->length + 1);
1051
1052
0
    std::memcpy(dp.get(), data, next_key->length);
1053
1054
0
    keys.emplace_back(key{{next_key->uuid}, std::move(dp), next_key->type});
1055
1056
    // Calculate address of next key (ensuring alignment)
1057
0
    const auto nk_addr =
1058
0
        (uintptr_t)next_key +
1059
0
        ((sizeof(*next_key) + next_key->length + 0x0F) & ~0x0FULL);
1060
1061
0
    next_key = reinterpret_cast<const apfs_keybag_key*>(nk_addr);
1062
0
  }
1063
1064
0
  return keys;
1065
0
}
1066
1067
APFSSuperblock::Keybag::Keybag(const APFSSuperblock& sb)
1068
0
    : APFSKeybag(sb.pool(), sb.sb()->keylocker.start_paddr, sb.sb()->uuid,
1069
0
                 sb.sb()->uuid) {
1070
0
  if (obj_type_and_flags() != APFS_OBJ_TYPE_CONTAINER_KEYBAG) {
1071
0
    throw std::runtime_error("APFSSuperblock::Keybag: invalid object type");
1072
0
  }
1073
1074
0
  if (sb.sb()->keylocker.block_count != 1) {
1075
0
    throw std::runtime_error("only single block keybags are supported");
1076
0
  }
1077
0
}
1078
1079
APFSExtentRefBtreeNode::iterator APFSExtentRefBtreeNode::find(
1080
0
    apfs_block_num block) const {
1081
0
  return APFSBtreeNode::find(
1082
0
      block, [](const auto& key, const auto block) noexcept->int64_t {
1083
0
        return key.template as<APFSPhysicalExtentKey>()->start_block() - block;
1084
0
      });
1085
0
}
1086
1087
APFSFileSystem::Keybag::Keybag(const APFSFileSystem& vol,
1088
                               apfs_block_num block_num)
1089
0
    : APFSKeybag(vol.pool(), block_num, vol.fs()->uuid, vol.fs()->uuid) {
1090
0
  if (obj_type_and_flags() != APFS_OBJ_TYPE_VOLUME_RECOVERY_KEYBAG) {
1091
0
    throw std::runtime_error("APFSFileSystem::Keybag: invalid object type");
1092
0
  }
1093
0
}