blob: 111edf39bc64ac63feb28443b9a131e812fc91f2 [file] [log] [blame]
/*
* 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 }
}
};