mirror of
https://gitlab.com/cryptsetup/cryptsetup.git
synced 2025-12-12 03:10:08 +01:00
Add support for parsing BitLocker metadata
Currently only support for metadata version 2 is implemented.
This commit is contained in:
committed by
Milan Broz
parent
434fee2e13
commit
62c872eb49
@@ -3,6 +3,7 @@
|
||||
*
|
||||
* Copyright (C) 2019 Red Hat, Inc. All rights reserved.
|
||||
* Copyright (C) 2019 Milan Broz
|
||||
* Copyright (C) 2019 Vojtech Trefny
|
||||
*
|
||||
* This file is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
@@ -25,25 +26,583 @@
|
||||
#include <string.h>
|
||||
#include <fcntl.h>
|
||||
#include <assert.h>
|
||||
#include <uuid/uuid.h>
|
||||
#include <time.h>
|
||||
#include <iconv.h>
|
||||
|
||||
#include "libcryptsetup.h"
|
||||
#include "bitlk.h"
|
||||
#include "internal.h"
|
||||
#include "bitops.h"
|
||||
|
||||
int BITLK_read_sb(struct crypt_device *cd, struct crypt_params_bitlk *params)
|
||||
#define BITLK_BOOTCODE_V1 "\xeb\x52\x90"
|
||||
#define BITLK_BOOTCODE_V2 "\xeb\x58\x90"
|
||||
#define BITLK_SIGNATURE "-FVE-FS-"
|
||||
#define BITLK_SIGNATURE_TOGO "MSWIN4.1"
|
||||
#define BITLK_HEADER_METADATA_OFFSET 160
|
||||
#define BITLK_HEADER_METADATA_OFFSET_TOGO 424
|
||||
|
||||
#define BITLK_FVE_METADATA_HEADER_LEN 64 + 48
|
||||
#define BITLK_FVE_METADATA_SIZE 64 * 1024
|
||||
#define BITLK_ENTRY_HEADER_LEN 8
|
||||
#define BITLK_VMK_HEADER_LEN 28
|
||||
|
||||
/* January 1, 1970 as MS file time */
|
||||
#define EPOCH_AS_FILETIME 116444736000000000
|
||||
#define HUNDREDS_OF_NANOSECONDS 10000000
|
||||
|
||||
/* taken from libfdisk gpt.c -- TODO: this is a good candidate for adding to libuuid */
|
||||
struct bitlk_guid {
|
||||
uint32_t time_low;
|
||||
uint16_t time_mid;
|
||||
uint16_t time_hi_and_version;
|
||||
uint8_t clock_seq_hi;
|
||||
uint8_t clock_seq_low;
|
||||
uint8_t node[6];
|
||||
} __attribute__ ((packed));
|
||||
|
||||
static void swap_guid(struct bitlk_guid *guid) {
|
||||
guid->time_low = swab32(guid->time_low);
|
||||
guid->time_mid = swab16(guid->time_mid);
|
||||
guid->time_hi_and_version = swab16(guid->time_hi_and_version);
|
||||
}
|
||||
|
||||
static void guid_to_string(struct bitlk_guid *guid, char *out) {
|
||||
swap_guid(guid);
|
||||
uuid_unparse((unsigned char *) guid, out);
|
||||
}
|
||||
|
||||
struct bitlk_signature {
|
||||
uint8_t boot_code[3];
|
||||
uint8_t signature[8];
|
||||
} __attribute__ ((packed));
|
||||
|
||||
struct bitlk_superblock {
|
||||
struct bitlk_guid guid;
|
||||
uint64_t fve_offset[3];
|
||||
} __attribute__ ((packed));
|
||||
|
||||
struct bitlk_fve_metadata {
|
||||
uint8_t signature[8];
|
||||
uint16_t fve_size;
|
||||
uint16_t fve_version;
|
||||
uint32_t unknown;
|
||||
uint64_t volume_size;
|
||||
uint32_t unknown2;
|
||||
uint32_t volume_header_size;
|
||||
uint64_t fve_offset[3];
|
||||
uint64_t volume_header_offset;
|
||||
uint32_t metadata_size;
|
||||
uint32_t metadata_version;
|
||||
uint32_t metadata_header_size;
|
||||
uint32_t metada_size_copy;
|
||||
struct bitlk_guid guid;
|
||||
uint32_t next_nonce;
|
||||
uint16_t encryption;
|
||||
uint16_t unknown3;
|
||||
uint64_t creation_time;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
struct bitlk_entry_header_block {
|
||||
uint64_t offset;
|
||||
uint64_t size;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
struct bitlk_entry_vmk {
|
||||
struct bitlk_guid guid;
|
||||
uint8_t modified[8];
|
||||
uint16_t _unknown;
|
||||
uint16_t protection;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
static BITLKVMKProtection get_vmk_protection(uint16_t protection)
|
||||
{
|
||||
int devfd;
|
||||
switch (protection) {
|
||||
case 0x0000:
|
||||
return BITLK_PROTECTION_CLEAR_KEY;
|
||||
case 0x0100:
|
||||
return BITLK_PROTECTION_TPM;
|
||||
case 0x0200:
|
||||
return BITLK_PROTECTION_STARTUP_KEY;
|
||||
case 0x0500:
|
||||
return BITLK_PROTECTION_TPM_PIN;
|
||||
case 0x0800:
|
||||
return BITLK_PROTECTION_RECOVERY_PASSPHRASE;
|
||||
case 0x2000:
|
||||
return BITLK_PROTECTION_PASSPHRASE;
|
||||
default:
|
||||
return BITLK_PROTECTION_UNKNOWN;
|
||||
}
|
||||
}
|
||||
|
||||
devfd = device_open(cd, crypt_data_device(cd), O_RDONLY);
|
||||
if(devfd < 0)
|
||||
return -EINVAL;
|
||||
static const char* get_vmk_protection_string(BITLKVMKProtection protection)
|
||||
{
|
||||
switch (protection) {
|
||||
case BITLK_PROTECTION_CLEAR_KEY:
|
||||
return "VMK protected with clear key";
|
||||
case BITLK_PROTECTION_TPM:
|
||||
return "VMK protected with TPM";
|
||||
case BITLK_PROTECTION_STARTUP_KEY:
|
||||
return "VMK protected with startup key";
|
||||
case BITLK_PROTECTION_TPM_PIN:
|
||||
return "VMK protected with TPM and PIN";
|
||||
case BITLK_PROTECTION_PASSPHRASE:
|
||||
return "VMK protected with passphrase";
|
||||
case BITLK_PROTECTION_RECOVERY_PASSPHRASE:
|
||||
return "VMK protected with recovery passphrase";
|
||||
default:
|
||||
return "VMK with unknown protection";
|
||||
}
|
||||
}
|
||||
|
||||
/* TODO -- move to some utils file */
|
||||
static void hexprint(struct crypt_device *cd, const char *d, int n, const char *sep)
|
||||
{
|
||||
int i;
|
||||
for(i = 0; i < n; i++)
|
||||
log_std(cd, "%02hhx%s", (const char)d[i], sep);
|
||||
}
|
||||
|
||||
static uint64_t filetime_to_unixtime(uint64_t time)
|
||||
{
|
||||
return (time - EPOCH_AS_FILETIME) / HUNDREDS_OF_NANOSECONDS;
|
||||
}
|
||||
|
||||
static int convert_to_utf8(struct crypt_device *cd, uint8_t *input, size_t inlen, char **out)
|
||||
{
|
||||
char *outbuf = NULL;
|
||||
iconv_t ic;
|
||||
size_t ic_inlen = inlen;
|
||||
size_t ic_outlen = inlen;
|
||||
char *ic_outbuf = NULL;
|
||||
size_t r = 0;
|
||||
|
||||
outbuf = malloc(inlen);
|
||||
if (outbuf == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
memset(outbuf, 0, inlen);
|
||||
ic_outbuf = outbuf;
|
||||
|
||||
ic = iconv_open("UTF-8", "UTF-16");
|
||||
r = iconv(ic, (char **) &input, &ic_inlen, &ic_outbuf, &ic_outlen);
|
||||
iconv_close(ic);
|
||||
|
||||
if (r == 0)
|
||||
*out = strdup(outbuf);
|
||||
else {
|
||||
*out = NULL;
|
||||
log_dbg(cd, "Failed to covert volume description: %s", strerror(errno));
|
||||
r = 0;
|
||||
}
|
||||
|
||||
free(outbuf);
|
||||
return r;
|
||||
}
|
||||
|
||||
static int parse_vmk_entry(struct crypt_device *cd, uint8_t *data, int start, int end, struct bitlk_vmk **vmk)
|
||||
{
|
||||
uint16_t key_entry_size = 0;
|
||||
uint16_t key_entry_type = 0;
|
||||
uint16_t key_entry_value = 0;
|
||||
size_t key_size = 0;
|
||||
const char *key = NULL;
|
||||
struct volume_key *vk = NULL;
|
||||
|
||||
while (end - start > 2) {
|
||||
/* size of this entry */
|
||||
memcpy(&key_entry_size, data + start, sizeof(key_entry_size));
|
||||
if (key_entry_size == 0)
|
||||
break;
|
||||
|
||||
/* type and value of this entry */
|
||||
memcpy(&key_entry_type, data + start + sizeof(key_entry_size), sizeof(key_entry_type));
|
||||
memcpy(&key_entry_value,
|
||||
data + start + sizeof(key_entry_size) + sizeof(key_entry_type),
|
||||
sizeof(key_entry_value));
|
||||
|
||||
if (key_entry_type != BITLK_ENTRY_TYPE_PROPERTY) {
|
||||
log_err(cd, _("Unexpected metadata entry found when parsing VMK."));
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* stretch key with salt, skip 4 B (encryption method of the stretch key) */
|
||||
if (key_entry_value == BITLK_ENTRY_VALUE_STRETCH_KEY)
|
||||
memcpy((*vmk)->salt,
|
||||
data + start + BITLK_ENTRY_HEADER_LEN + 4,
|
||||
sizeof((*vmk)->salt));
|
||||
/* AES-CCM encrypted key */
|
||||
else if (key_entry_value == BITLK_ENTRY_VALUE_ENCRYPTED_KEY) {
|
||||
/* nonce */
|
||||
memcpy((*vmk)->nonce,
|
||||
data + start + BITLK_ENTRY_HEADER_LEN,
|
||||
sizeof((*vmk)->nonce));
|
||||
/* MAC tag */
|
||||
memcpy((*vmk)->mac_tag,
|
||||
data + start + BITLK_ENTRY_HEADER_LEN + BITLK_NONCE_SIZE,
|
||||
sizeof((*vmk)->mac_tag));
|
||||
/* AES-CCM encrypted key */
|
||||
key_size = key_entry_size - (BITLK_ENTRY_HEADER_LEN + BITLK_NONCE_SIZE + BITLK_VMK_MAC_TAG_SIZE);
|
||||
key = (const char *) data + start + BITLK_ENTRY_HEADER_LEN + BITLK_NONCE_SIZE + BITLK_VMK_MAC_TAG_SIZE;
|
||||
vk = crypt_alloc_volume_key(key_size, key);
|
||||
if (vk == NULL)
|
||||
return -ENOMEM;
|
||||
crypt_volume_key_add_next(&((*vmk)->vk), vk);
|
||||
/* clear key for a partially decrypted volume */
|
||||
} else if (key_entry_value == BITLK_ENTRY_VALUE_KEY) {
|
||||
/* We currently don't want to support opening a partially decrypted
|
||||
* device so we don't need to store this key.
|
||||
*
|
||||
* key_size = key_entry_size - (BITLK_ENTRY_HEADER_LEN + 4);
|
||||
* key = (const char *) data + start + BITLK_ENTRY_HEADER_LEN + 4;
|
||||
* vk = crypt_alloc_volume_key(key_size, key);
|
||||
* if (vk == NULL)
|
||||
* return -ENOMEM;
|
||||
* crypt_volume_key_add_next(&((*vmk)->vk), vk);
|
||||
*/
|
||||
log_dbg(cd, "Skipping clear key metadata entry.");
|
||||
} else {
|
||||
log_err(cd, _("Unexpected metadata entry found when parsing VMK."));
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
start += key_entry_size;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int BITLK_dump(struct crypt_device *cd, struct device *device)
|
||||
void BITLK_bitlk_fvek_free(struct bitlk_fvek *fvek)
|
||||
{
|
||||
log_std(cd, "Info for BITLK device %s.\n", device_path(device));
|
||||
if (!fvek)
|
||||
return;
|
||||
|
||||
crypt_free_volume_key(fvek->vk);
|
||||
}
|
||||
|
||||
void BITLK_bitlk_vmk_free(struct bitlk_vmk *vmk)
|
||||
{
|
||||
struct bitlk_vmk *vmk_next = NULL;
|
||||
|
||||
while (vmk) {
|
||||
if (vmk->guid)
|
||||
free(vmk->guid);
|
||||
crypt_free_volume_key(vmk->vk);
|
||||
vmk_next = vmk->next;
|
||||
free(vmk);
|
||||
vmk = vmk_next;
|
||||
}
|
||||
}
|
||||
|
||||
void BITLK_bitlk_metadata_free(struct bitlk_metadata *metadata)
|
||||
{
|
||||
free(metadata->guid);
|
||||
if (metadata->description)
|
||||
free(metadata->description);
|
||||
BITLK_bitlk_vmk_free(metadata->vmks);
|
||||
BITLK_bitlk_fvek_free(metadata->fvek);
|
||||
}
|
||||
|
||||
int BITLK_read_sb(struct crypt_device *cd, struct bitlk_metadata *params)
|
||||
{
|
||||
int devfd;
|
||||
struct device *device = crypt_metadata_device(cd);
|
||||
struct bitlk_signature sig = {0};
|
||||
struct bitlk_superblock sb = {0};
|
||||
struct bitlk_fve_metadata fve = {0};
|
||||
struct bitlk_entry_vmk entry_vmk = {0};
|
||||
uint8_t *fve_entries = NULL;
|
||||
int fve_offset = 0;
|
||||
char guid_buf[UUID_STR_LEN] = {0};
|
||||
uint16_t entry_size = 0;
|
||||
uint16_t entry_type = 0;
|
||||
int r = 0;
|
||||
int start = 0;
|
||||
int end = 0;
|
||||
size_t key_size = 0;
|
||||
const char *key = NULL;
|
||||
|
||||
struct bitlk_vmk *vmk = NULL;
|
||||
struct bitlk_vmk *vmk_p = params->vmks;
|
||||
|
||||
devfd = device_open(cd, crypt_data_device(cd), O_RDONLY);
|
||||
if (devfd < 0) {
|
||||
r = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* read and check the BitLocker signature */
|
||||
if (read_lseek_blockwise(devfd, device_block_size(cd, device),
|
||||
device_alignment(device), &sig, sizeof(sig), 0) != sizeof(sig)) {
|
||||
log_err(cd, _("Failed to read bitlocker signature from %s."), device_path(device));
|
||||
r = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (memcmp(sig.boot_code, BITLK_BOOTCODE_V1, sizeof(sig.boot_code)) == 0) {
|
||||
log_err(cd, _("BitLocker version from Windows Vista is currently not supported"));
|
||||
r = -ENOTSUP;
|
||||
goto out;
|
||||
} else if (memcmp(sig.boot_code, BITLK_BOOTCODE_V2, sizeof(sig.boot_code)) == 0)
|
||||
;
|
||||
else {
|
||||
log_std(cd, _("Invalid or unknown boot signature for a BitLocker device."));
|
||||
r = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (memcmp(sig.signature, BITLK_SIGNATURE, sizeof(sig.signature)) == 0) {
|
||||
params->togo = false;
|
||||
fve_offset = BITLK_HEADER_METADATA_OFFSET;
|
||||
} else if (memcmp(sig.signature, BITLK_SIGNATURE_TOGO, sizeof(sig.signature)) == 0) {
|
||||
params->togo = true;
|
||||
fve_offset = BITLK_HEADER_METADATA_OFFSET_TOGO;
|
||||
} else {
|
||||
log_std(cd, _("Invalid or unknown signature for a BitLocker device."));
|
||||
r = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* read GUID and FVE metadata offsets */
|
||||
if (read_lseek_blockwise(devfd, device_block_size(cd, device),
|
||||
device_alignment(device), &sb, sizeof(sb), fve_offset) != sizeof(sb)) {
|
||||
log_err(cd, _("Failed to read BitLocker header from %s."), device_path(device));
|
||||
r = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
log_dbg(cd, "Reading BitLocker FVE metadata of size %zu on device %s, offset %" PRIu64 ".",
|
||||
sizeof(fve), device_path(device), sb.fve_offset[0]);
|
||||
|
||||
/* read FVE metadata from the first metadata area */
|
||||
if (read_lseek_blockwise(devfd, device_block_size(cd, device),
|
||||
device_alignment(device), &fve, sizeof(fve), sb.fve_offset[0]) != sizeof(fve) ||
|
||||
memcmp(fve.signature, BITLK_SIGNATURE, sizeof(fve.signature)) ||
|
||||
fve.fve_version != 2) {
|
||||
log_err(cd, _("Failed to read BitLocker FVE metadata from %s."), device_path(device));
|
||||
r = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
params->metadata_version = le32_to_cpu(fve.fve_version);
|
||||
for (int i = 0; i < 3; i++)
|
||||
params->metadata_offset[i] = le64_to_cpu(sb.fve_offset[i]);
|
||||
|
||||
switch (fve.encryption) {
|
||||
/* AES-CBC with Elephant difuser */
|
||||
case 0x8000:
|
||||
params->key_size = 128;
|
||||
params->cipher = "aes";
|
||||
params->cipher_mode = "cbc-elephant";
|
||||
break;
|
||||
case 0x8001:
|
||||
params->key_size = 256;
|
||||
params->cipher = "aes";
|
||||
params->cipher_mode = "cbc-elephant";
|
||||
break;
|
||||
/* AES-CBC */
|
||||
case 0x8002:
|
||||
params->key_size = 128;
|
||||
params->cipher = "aes";
|
||||
params->cipher_mode = "cbc-eboiv";
|
||||
break;
|
||||
case 0x8003:
|
||||
params->key_size = 256;
|
||||
params->cipher = "aes";
|
||||
params->cipher_mode = "cbc-eboiv";
|
||||
break;
|
||||
/* AES-XTS */
|
||||
case 0x8004:
|
||||
params->key_size = 128;
|
||||
params->cipher = "aes";
|
||||
params->cipher_mode = "xts-plain64";
|
||||
break;
|
||||
case 0x8005:
|
||||
params->key_size = 256;
|
||||
params->cipher = "aes";
|
||||
params->cipher_mode = "xts-plain64";
|
||||
break;
|
||||
default:
|
||||
log_err (cd, _("Unknown or unsupported encryption"));
|
||||
params->key_size = 0;
|
||||
params->cipher = NULL;
|
||||
params->cipher_mode = NULL;
|
||||
r = -ENOTSUP;
|
||||
goto out;
|
||||
};
|
||||
|
||||
/* BitLocker device GUID */
|
||||
guid_to_string(&fve.guid, guid_buf);
|
||||
params->guid = strdup(guid_buf);
|
||||
if (!params->guid) {
|
||||
r = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
|
||||
params->creation_time = filetime_to_unixtime(le64_to_cpu(fve.creation_time));
|
||||
|
||||
/* read and parse all FVE metadata entries */
|
||||
fve_entries = malloc(fve.metadata_size - fve.fve_size);
|
||||
if (!fve_entries) {
|
||||
r = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
memset(fve_entries, 0, (fve.metadata_size - fve.fve_size));
|
||||
|
||||
log_dbg(cd, "Reading BitLocker FVE metadata entries of size %" PRIu32 " on device %s, offset %" PRIu64 ".",
|
||||
fve.metadata_size - fve.fve_size, device_path(device),
|
||||
sb.fve_offset[0] + BITLK_FVE_METADATA_HEADER_LEN);
|
||||
|
||||
if (read_lseek_blockwise(devfd, device_block_size(cd, device),
|
||||
device_alignment(device), fve_entries, fve.metadata_size - fve.fve_size,
|
||||
sb.fve_offset[0] + BITLK_FVE_METADATA_HEADER_LEN) != fve.metadata_size - fve.fve_size) {
|
||||
log_err(cd, _("Failed to read BitLocker metadata entries from %s."), device_path(device));
|
||||
r = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
end = fve.metadata_size - fve.fve_size;
|
||||
while (end - start > 2) {
|
||||
/* size of this entry */
|
||||
memcpy(&entry_size, fve_entries + start, sizeof(entry_size));
|
||||
if (entry_size == 0)
|
||||
break;
|
||||
|
||||
/* type of this entry */
|
||||
memcpy(&entry_type, fve_entries + start + sizeof(entry_size), sizeof(entry_type));
|
||||
|
||||
/* VMK */
|
||||
if (entry_type == BITLK_ENTRY_TYPE_VMK) {
|
||||
/* skip first four variables in the entry (entry size, type, value and version) */
|
||||
memcpy(&entry_vmk,
|
||||
fve_entries + start + BITLK_ENTRY_HEADER_LEN,
|
||||
sizeof(entry_vmk));
|
||||
|
||||
vmk = malloc(sizeof(struct bitlk_vmk));
|
||||
memset(vmk, 0, sizeof(struct bitlk_vmk));
|
||||
|
||||
guid_to_string(&entry_vmk.guid, guid_buf);
|
||||
vmk->guid = strdup (guid_buf);
|
||||
|
||||
vmk->protection = get_vmk_protection(entry_vmk.protection);
|
||||
|
||||
/* more data in another entry list */
|
||||
r = parse_vmk_entry(cd, fve_entries,
|
||||
start + BITLK_ENTRY_HEADER_LEN + BITLK_VMK_HEADER_LEN,
|
||||
start + entry_size, &vmk);
|
||||
if (r < 0) {
|
||||
BITLK_bitlk_vmk_free(vmk);
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (params->vmks == NULL)
|
||||
params->vmks = vmk;
|
||||
else
|
||||
vmk_p->next = vmk;
|
||||
|
||||
vmk_p = vmk;
|
||||
vmk = vmk->next;
|
||||
/* FVEK */
|
||||
} else if (entry_type == BITLK_ENTRY_TYPE_FVEK) {
|
||||
params->fvek = malloc(sizeof(struct bitlk_fvek));
|
||||
memcpy(params->fvek->nonce,
|
||||
fve_entries + start + BITLK_ENTRY_HEADER_LEN,
|
||||
sizeof(params->fvek->nonce));
|
||||
/* MAC tag */
|
||||
memcpy(params->fvek->mac_tag,
|
||||
fve_entries + start + BITLK_ENTRY_HEADER_LEN + BITLK_NONCE_SIZE,
|
||||
sizeof(params->fvek->mac_tag));
|
||||
/* AES-CCM encrypted key */
|
||||
key_size = entry_size - (BITLK_ENTRY_HEADER_LEN + BITLK_NONCE_SIZE + BITLK_VMK_MAC_TAG_SIZE);
|
||||
key = (const char *) fve_entries + start + BITLK_ENTRY_HEADER_LEN + BITLK_NONCE_SIZE + BITLK_VMK_MAC_TAG_SIZE;
|
||||
params->fvek->vk = crypt_alloc_volume_key(key_size, key);
|
||||
if (params->fvek->vk == NULL) {
|
||||
r = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
/* volume header info (location and size) */
|
||||
} else if (entry_type == BITLK_ENTRY_TYPE_VOLUME_HEADER) {
|
||||
struct bitlk_entry_header_block entry_header;
|
||||
memcpy(&entry_header,
|
||||
fve_entries + start + BITLK_ENTRY_HEADER_LEN,
|
||||
sizeof(entry_header));
|
||||
params->volume_header_offset = le64_to_cpu(entry_header.offset);
|
||||
params->volume_header_size = le64_to_cpu(entry_header.size);
|
||||
/* volume description (utf-16 string) */
|
||||
} else if (entry_type == BITLK_ENTRY_TYPE_DESCRIPTION) {
|
||||
r = convert_to_utf8(cd, fve_entries + start + BITLK_ENTRY_HEADER_LEN,
|
||||
entry_size - BITLK_ENTRY_HEADER_LEN,
|
||||
&(params->description));
|
||||
if (r < 0) {
|
||||
BITLK_bitlk_vmk_free(vmk);
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
start += entry_size;
|
||||
}
|
||||
|
||||
out:
|
||||
if (fve_entries)
|
||||
free(fve_entries);
|
||||
return r;
|
||||
}
|
||||
|
||||
int BITLK_dump(struct crypt_device *cd, struct device *device, struct bitlk_metadata *params)
|
||||
{
|
||||
struct volume_key *vk_p;
|
||||
struct bitlk_vmk *vmk_p;
|
||||
int next_id = 0;
|
||||
int i = 0;
|
||||
|
||||
log_std(cd, "Info for BITLK%s device %s.\n", params->togo ? " To Go" : "", device_path(device));
|
||||
log_std(cd, "Version: \t%u\n", params->metadata_version);
|
||||
log_std(cd, "GUID: \t%s\n", params->guid);
|
||||
log_std(cd, "Created: \t%s", ctime((time_t *)&(params->creation_time)));
|
||||
log_std(cd, "Description: \t%s\n", params->description);
|
||||
log_std(cd, "Cipher name: \t%s\n", params->cipher);
|
||||
log_std(cd, "Cipher mode: \t%s\n", params->cipher_mode);
|
||||
log_std(cd, "Cipher key: \t%u bits\n", params->key_size);
|
||||
|
||||
log_std(cd, "\n");
|
||||
|
||||
log_std(cd, "Keyslots:\n");
|
||||
vmk_p = params->vmks;
|
||||
while (vmk_p) {
|
||||
log_std(cd, " %d: VMK\n", next_id);
|
||||
log_std(cd, "\tGUID: \t%s\n", vmk_p->guid);
|
||||
log_std(cd, "\tProtection: \t%s\n", get_vmk_protection_string (vmk_p->protection));
|
||||
log_std(cd, "\tSalt: \t");
|
||||
hexprint(cd, (const char *) vmk_p->salt, 16, "");
|
||||
|
||||
vk_p = params->vmks->vk;
|
||||
while (vk_p) {
|
||||
log_std(cd, "\n");
|
||||
log_std(cd, "\tKey data size:\t%zu [bytes]\n", vk_p->keylength);
|
||||
vk_p = vk_p->next;
|
||||
}
|
||||
vmk_p = vmk_p->next;
|
||||
next_id++;
|
||||
}
|
||||
|
||||
log_std(cd, " %d: FVEK\n", next_id);
|
||||
log_std(cd, "\tKey data size:\t%zu [bytes]\n", params->fvek->vk->keylength);
|
||||
|
||||
log_std(cd, "\n");
|
||||
|
||||
log_std(cd, "Metadata segments:\n");
|
||||
|
||||
for (i = 0; i < 3; i++) {
|
||||
log_std(cd, " %d: FVE metadata area\n", i);
|
||||
log_std(cd, "\tOffset: \t%" PRIu64 " [bytes]\n", params->metadata_offset[i]);
|
||||
log_std(cd, "\tSize: \t%d [bytes]\n", BITLK_FVE_METADATA_SIZE);
|
||||
}
|
||||
|
||||
log_std(cd, " %d: Volume header\n", i);
|
||||
log_std(cd, "\tOffset: \t%" PRIu64 " [bytes]\n", params->volume_header_offset);
|
||||
log_std(cd, "\tSize: \t%" PRIu64 " [bytes]\n", params->volume_header_size);
|
||||
log_std(cd, "\tCipher: \t%s-%s\n", params->cipher, params->cipher_mode);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -51,7 +610,7 @@ int BITLK_activate(struct crypt_device *cd,
|
||||
const char *name,
|
||||
const char *password,
|
||||
size_t passwordLen,
|
||||
const struct crypt_params_bitlk *params,
|
||||
const struct bitlk_metadata *params,
|
||||
uint32_t flags)
|
||||
{
|
||||
int r;
|
||||
@@ -75,7 +634,7 @@ int BITLK_activate(struct crypt_device *cd,
|
||||
|
||||
/* First sector mapped to DM_LINEAR */
|
||||
r = dm_linear_target_set(&dmd.segment, 0, 1, crypt_data_device(cd), 0);
|
||||
if (r)
|
||||
if (r)
|
||||
goto out;
|
||||
|
||||
/* The rest is mapped to DM_ZERO (for now) */
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
*
|
||||
* Copyright (C) 2019 Red Hat, Inc. All rights reserved.
|
||||
* Copyright (C) 2019 Milan Broz
|
||||
* Copyright (C) 2019 Vojtech Trefny
|
||||
*
|
||||
* This file is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
@@ -25,19 +26,96 @@
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
|
||||
#include "internal.h"
|
||||
|
||||
struct crypt_device;
|
||||
struct device;
|
||||
struct crypt_params_bitlk;
|
||||
|
||||
int BITLK_read_sb(struct crypt_device *cd, struct crypt_params_bitlk *params);
|
||||
#define BITLK_NONCE_SIZE 12
|
||||
#define BITLK_SALT_SIZE 16
|
||||
#define BITLK_VMK_MAC_TAG_SIZE 16
|
||||
|
||||
int BITLK_dump(struct crypt_device *cd, struct device *device);
|
||||
#define BITLK_VMK_OPEN_KEY "openkey"
|
||||
|
||||
typedef enum {
|
||||
BITLK_PROTECTION_CLEAR_KEY = 0,
|
||||
BITLK_PROTECTION_TPM,
|
||||
BITLK_PROTECTION_STARTUP_KEY,
|
||||
BITLK_PROTECTION_TPM_PIN,
|
||||
BITLK_PROTECTION_RECOVERY_PASSPHRASE,
|
||||
BITLK_PROTECTION_PASSPHRASE,
|
||||
BITLK_PROTECTION_UNKNOWN,
|
||||
} BITLKVMKProtection;
|
||||
|
||||
typedef enum {
|
||||
BITLK_ENTRY_TYPE_PROPERTY = 0x0000,
|
||||
BITLK_ENTRY_TYPE_VMK = 0x0002,
|
||||
BITLK_ENTRY_TYPE_FVEK = 0x0003,
|
||||
BITLK_ENTRY_TYPE_STARTUP_KEY = 0x0006,
|
||||
BITLK_ENTRY_TYPE_DESCRIPTION = 0x0007,
|
||||
BITLK_ENTRY_TYPE_VOLUME_HEADER = 0x000f,
|
||||
} BITLKFVEEntryType;
|
||||
|
||||
typedef enum {
|
||||
BITLK_ENTRY_VALUE_ERASED = 0x0000,
|
||||
BITLK_ENTRY_VALUE_KEY = 0x0001,
|
||||
BITLK_ENTRY_VALUE_STRING = 0x0002,
|
||||
BITLK_ENTRY_VALUE_STRETCH_KEY = 0x0003,
|
||||
BITLK_ENTRY_VALUE_USE_KEY = 0x0004,
|
||||
BITLK_ENTRY_VALUE_ENCRYPTED_KEY = 0x0005,
|
||||
BITLK_ENTRY_VALUE_TPM_KEY = 0x0006,
|
||||
BITLK_ENTRY_VALUE_VALIDATION = 0x0007,
|
||||
BITLK_ENTRY_VALUE_VMK = 0x0008,
|
||||
BITLK_ENTRY_VALUE_EXTERNAL_KEY = 0x0009,
|
||||
BITLK_ENTRY_VALUE_OFFSET_SIZE = 0x000f,
|
||||
} BITLKFVEEntryValue;
|
||||
|
||||
struct bitlk_vmk {
|
||||
char *guid;
|
||||
BITLKVMKProtection protection;
|
||||
uint8_t salt[BITLK_SALT_SIZE];
|
||||
uint8_t mac_tag[BITLK_VMK_MAC_TAG_SIZE];
|
||||
uint8_t nonce[BITLK_NONCE_SIZE];
|
||||
struct volume_key *vk;
|
||||
struct bitlk_vmk *next;
|
||||
};
|
||||
|
||||
struct bitlk_fvek {
|
||||
uint8_t mac_tag[BITLK_VMK_MAC_TAG_SIZE];
|
||||
uint8_t nonce[BITLK_NONCE_SIZE];
|
||||
struct volume_key *vk;
|
||||
};
|
||||
|
||||
struct bitlk_metadata {
|
||||
bool togo;
|
||||
const char *cipher;
|
||||
const char *cipher_mode;
|
||||
uint16_t key_size;
|
||||
char *guid;
|
||||
uint64_t creation_time;
|
||||
char *description;
|
||||
uint64_t metadata_offset[3];
|
||||
uint32_t metadata_version;
|
||||
uint64_t volume_header_offset;
|
||||
uint64_t volume_header_size;
|
||||
struct bitlk_vmk *vmks;
|
||||
struct bitlk_fvek *fvek;
|
||||
};
|
||||
|
||||
int BITLK_read_sb(struct crypt_device *cd, struct bitlk_metadata *params);
|
||||
|
||||
int BITLK_dump(struct crypt_device *cd, struct device *device, struct bitlk_metadata *params);
|
||||
|
||||
int BITLK_activate(struct crypt_device *cd,
|
||||
const char *name,
|
||||
const char *password,
|
||||
size_t passwordLen,
|
||||
const struct crypt_params_bitlk *params,
|
||||
const struct bitlk_metadata *params,
|
||||
uint32_t flags);
|
||||
|
||||
void BITLK_bitlk_fvek_free(struct bitlk_fvek *fvek);
|
||||
void BITLK_bitlk_vmk_free(struct bitlk_vmk *vmk);
|
||||
void BITLK_bitlk_metadata_free(struct bitlk_metadata *params);
|
||||
|
||||
#endif
|
||||
|
||||
30
lib/setup.c
30
lib/setup.c
@@ -113,7 +113,8 @@ struct crypt_device {
|
||||
uint32_t sb_flags;
|
||||
} integrity;
|
||||
struct { /* used in CRYPT_BITLK */
|
||||
struct crypt_params_bitlk params;
|
||||
struct bitlk_metadata params;
|
||||
char *cipher_spec;
|
||||
} bitlk;
|
||||
struct { /* used if initialized without header by name */
|
||||
char *active_name;
|
||||
@@ -974,7 +975,7 @@ static int _crypt_load_integrity(struct crypt_device *cd,
|
||||
}
|
||||
|
||||
static int _crypt_load_bitlk(struct crypt_device *cd,
|
||||
struct crypt_params_bitlk *params)
|
||||
struct bitlk_metadata *params)
|
||||
{
|
||||
int r;
|
||||
|
||||
@@ -986,6 +987,12 @@ static int _crypt_load_bitlk(struct crypt_device *cd,
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (asprintf(&cd->u.bitlk.cipher_spec, "%s-%s",
|
||||
cd->u.bitlk.params.cipher, cd->u.bitlk.params.cipher_mode) < 0) {
|
||||
cd->u.bitlk.cipher_spec = NULL;
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
if (!cd->type && !(cd->type = strdup(CRYPT_BITLK)))
|
||||
return -ENOMEM;
|
||||
|
||||
@@ -1128,6 +1135,9 @@ static void crypt_free_type(struct crypt_device *cd)
|
||||
free(CONST_CAST(void*)cd->u.integrity.params.journal_crypt);
|
||||
crypt_free_volume_key(cd->u.integrity.journal_crypt_key);
|
||||
crypt_free_volume_key(cd->u.integrity.journal_mac_key);
|
||||
} else if (isBITLK(cd->type)) {
|
||||
free(cd->u.bitlk.cipher_spec);
|
||||
BITLK_bitlk_metadata_free(&cd->u.bitlk.params);
|
||||
} else if (!cd->type) {
|
||||
free(cd->u.none.active_name);
|
||||
cd->u.none.active_name = NULL;
|
||||
@@ -4751,7 +4761,7 @@ int crypt_dump(struct crypt_device *cd)
|
||||
else if (isINTEGRITY(cd->type))
|
||||
return INTEGRITY_dump(cd, crypt_data_device(cd), 0);
|
||||
else if (isBITLK(cd->type))
|
||||
return BITLK_dump(cd, crypt_data_device(cd));
|
||||
return BITLK_dump(cd, crypt_data_device(cd), &cd->u.bitlk.params);
|
||||
|
||||
log_err(cd, _("Dump operation is not supported for this device type."));
|
||||
return -EINVAL;
|
||||
@@ -4770,6 +4780,8 @@ const char *crypt_get_cipher_spec(struct crypt_device *cd)
|
||||
return cd->u.plain.cipher_spec;
|
||||
else if (isLOOPAES(cd->type))
|
||||
return cd->u.loopaes.cipher_spec;
|
||||
else if (isBITLK(cd->type))
|
||||
return cd->u.bitlk.cipher_spec;
|
||||
else if (!cd->type && !_init_by_name_crypt_none(cd))
|
||||
return cd->u.none.cipher_spec;
|
||||
|
||||
@@ -4800,6 +4812,9 @@ const char *crypt_get_cipher(struct crypt_device *cd)
|
||||
if (isTCRYPT(cd->type))
|
||||
return cd->u.tcrypt.params.cipher;
|
||||
|
||||
if (isBITLK(cd->type))
|
||||
return cd->u.bitlk.params.cipher;
|
||||
|
||||
if (!cd->type && !_init_by_name_crypt_none(cd))
|
||||
return cd->u.none.cipher;
|
||||
|
||||
@@ -4830,6 +4845,9 @@ const char *crypt_get_cipher_mode(struct crypt_device *cd)
|
||||
if (isTCRYPT(cd->type))
|
||||
return cd->u.tcrypt.params.mode;
|
||||
|
||||
if (isBITLK(cd->type))
|
||||
return cd->u.bitlk.params.cipher_mode;
|
||||
|
||||
if (!cd->type && !_init_by_name_crypt_none(cd))
|
||||
return cd->u.none.cipher_mode;
|
||||
|
||||
@@ -4904,6 +4922,9 @@ const char *crypt_get_uuid(struct crypt_device *cd)
|
||||
if (isVERITY(cd->type))
|
||||
return cd->u.verity.uuid;
|
||||
|
||||
if (isBITLK(cd->type))
|
||||
return cd->u.bitlk.params.guid;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@@ -4964,6 +4985,9 @@ int crypt_get_volume_key_size(struct crypt_device *cd)
|
||||
if (isTCRYPT(cd->type))
|
||||
return cd->u.tcrypt.params.key_size;
|
||||
|
||||
if (isBITLK(cd->type))
|
||||
return cd->u.bitlk.params.key_size;
|
||||
|
||||
if (!cd->type && !_init_by_name_crypt_none(cd))
|
||||
return cd->u.none.key_size;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user