| /* |
| * Copyright (C) 2018 Karel Zak <kzak@redhat.com> |
| * |
| * This file may be redistributed under the terms of the |
| * GNU Lesser General Public License. |
| */ |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <unistd.h> |
| #include <string.h> |
| #include <errno.h> |
| #include <ctype.h> |
| #include <stdint.h> |
| |
| #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 } |
| } |
| }; |