/* * Copyright (C) 2018 Karel Zak * * This file may be redistributed under the terms of the * GNU Lesser General Public License. */ #include #include #include #include #include #include #include #include "superblocks.h" #define BDE_HDR_SIZE 512 #define BDE_HDR_OFFSET 0 struct bde_header_win7 { /* 0 */ unsigned char boot_entry_point[3]; /* 3 */ unsigned char fs_signature[8]; /* 11 */ unsigned char __dummy1[67 - 11]; /* 67 */ uint32_t volume_serial; /* NTFS uses 64bit serial number */ /* 71 */ unsigned char volume_label[11]; /* "NO NAME\x20\x20\x20\x20" only */ /* 82 */ unsigned char __dummy2[160 - 82]; /* 160 */ unsigned char guid[16]; /* BitLocker specific GUID */ /* 176 */ uint64_t fve_metadata_offset; } __attribute__((packed)); struct bde_header_togo { /* 0 */ unsigned char boot_entry_point[3]; /* 3 */ unsigned char fs_signature[8]; /* 11 */ unsigned char __dummy[424 - 11]; /* 424 */ unsigned char guid[16]; /* 440 */ uint64_t fve_metadata_offset; } __attribute__((packed)); struct bde_fve_metadata { /* 0 */ unsigned char signature[8]; /* 8 */ uint16_t size; /* 10 */ uint16_t version; }; enum { BDE_VERSION_VISTA = 0, BDE_VERSION_WIN7, BDE_VERSION_TOGO }; #define BDE_MAGIC_VISTA "\xeb\x52\x90-FVE-FS-" #define BDE_MAGIC_WIN7 "\xeb\x58\x90-FVE-FS-" #define BDE_MAGIC_TOGO "\xeb\x58\x90MSWIN4.1" #define BDE_MAGIC_FVE "-FVE-FS-" static int get_bitlocker_type(const unsigned char *buf) { size_t i; static const char *map[] = { [BDE_VERSION_VISTA] = BDE_MAGIC_VISTA, [BDE_VERSION_WIN7] = BDE_MAGIC_WIN7, [BDE_VERSION_TOGO] = BDE_MAGIC_TOGO }; for (i = 0; i < ARRAY_SIZE(map); i++) { if (memcmp(buf, map[i], 11) == 0) return (int) i; } return -1; } /* Returns: < 0 error, 1 nothing, 0 success */ static int get_bitlocker_headers(blkid_probe pr, int *type, const unsigned char **buf_hdr, const unsigned char **buf_fve) { const unsigned char *buf; const struct bde_fve_metadata *fve; uint64_t off = 0; int kind; if (buf_hdr) *buf_hdr = NULL; if (buf_fve) *buf_fve = NULL; if (type) *type = -1; buf = blkid_probe_get_buffer(pr, BDE_HDR_OFFSET, BDE_HDR_SIZE); if (!buf) return errno ? -errno : 1; kind = get_bitlocker_type(buf); /* Check BitLocker header */ switch (kind) { case BDE_VERSION_WIN7: off = le64_to_cpu(((const struct bde_header_win7 *) buf)->fve_metadata_offset); break; case BDE_VERSION_TOGO: off = le64_to_cpu(((const struct bde_header_togo *) buf)->fve_metadata_offset); break; case BDE_VERSION_VISTA: goto done; default: goto nothing; } if (!off) goto nothing; if (buf_hdr) *buf_hdr = buf; /* Check Bitlocker FVE metadata header */ buf = blkid_probe_get_buffer(pr, off, sizeof(struct bde_fve_metadata)); if (!buf) return errno ? -errno : 1; fve = (const struct bde_fve_metadata *) buf; if (memcmp(fve->signature, BDE_MAGIC_FVE, sizeof(fve->signature)) != 0) goto nothing; if (buf_fve) *buf_fve = buf; done: if (type) *type = kind; return 0; nothing: return 1; } /* * This is used by vFAT and NTFS prober to avoid collisions with bitlocker. */ int blkid_probe_is_bitlocker(blkid_probe pr) { return get_bitlocker_headers(pr, NULL, NULL, NULL) == 0; } static int probe_bitlocker(blkid_probe pr, const struct blkid_idmag *mag __attribute__((__unused__))) { const unsigned char *buf_fve = NULL; const unsigned char *buf_hdr = NULL; int rc, kind; rc = get_bitlocker_headers(pr, &kind, &buf_hdr, &buf_fve); if (rc) return rc; if (kind == BDE_VERSION_WIN7) { const struct bde_header_win7 *hdr = (const struct bde_header_win7 *) buf_hdr; /* Unfortunately, it seems volume_serial is always zero */ blkid_probe_sprintf_uuid(pr, (const unsigned char *) &hdr->volume_serial, sizeof(hdr->volume_serial), "%016d", le32_to_cpu(hdr->volume_serial)); } if (buf_fve) { const struct bde_fve_metadata *fve = (const struct bde_fve_metadata *) buf_fve; blkid_probe_sprintf_version(pr, "%d", fve->version); } return 0; } /* See header details: * https://github.com/libyal/libbde/blob/master/documentation/BitLocker%20Drive%20Encryption%20(BDE)%20format.asciidoc */ const struct blkid_idinfo bitlocker_idinfo = { .name = "BitLocker", .usage = BLKID_USAGE_CRYPTO, .probefunc = probe_bitlocker, .magics = { { .magic = BDE_MAGIC_VISTA, .len = 11 }, { .magic = BDE_MAGIC_WIN7, .len = 11 }, { .magic = BDE_MAGIC_TOGO, .len = 11 }, { NULL } } };