/*
* BITLK (BitLocker-compatible) volume handling
*
- * Copyright (C) 2019-2021 Red Hat, Inc. All rights reserved.
- * Copyright (C) 2019-2021 Milan Broz
- * Copyright (C) 2019-2021 Vojtech Trefny
+ * Copyright (C) 2019-2023 Red Hat, Inc. All rights reserved.
+ * Copyright (C) 2019-2023 Milan Broz
+ * Copyright (C) 2019-2023 Vojtech Trefny
*
* This file is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
#include <string.h>
#include <uuid/uuid.h>
#include <time.h>
-#include <iconv.h>
#include <limits.h>
#include "bitlk.h"
}
}
-/* 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-16LE");
- 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 convert volume description: %s", strerror(errno));
- r = 0;
- }
-
- free(outbuf);
- return r;
-}
-
-static int passphrase_to_utf16(struct crypt_device *cd, char *input, size_t inlen, char **out)
-{
- char *outbuf = NULL;
- iconv_t ic;
- size_t ic_inlen = inlen;
- size_t ic_outlen = inlen * 2;
- char *ic_outbuf = NULL;
- size_t r = 0;
-
- if (inlen == 0)
- return r;
-
- outbuf = crypt_safe_alloc(inlen * 2);
- if (outbuf == NULL)
- return -ENOMEM;
-
- memset(outbuf, 0, inlen * 2);
- ic_outbuf = outbuf;
-
- ic = iconv_open("UTF-16LE", "UTF-8");
- r = iconv(ic, &input, &ic_inlen, &ic_outbuf, &ic_outlen);
- iconv_close(ic);
-
- if (r == 0) {
- *out = outbuf;
- } else {
- *out = NULL;
- crypt_safe_free(outbuf);
- log_dbg(cd, "Failed to convert passphrase: %s", strerror(errno));
- r = -errno;
- }
-
- 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;
const char *key = NULL;
struct volume_key *vk = NULL;
bool supported = false;
+ int r = 0;
/* only passphrase or recovery passphrase vmks are supported (can be used to activate) */
supported = (*vmk)->protection == BITLK_PROTECTION_PASSPHRASE ||
(*vmk)->protection == BITLK_PROTECTION_RECOVERY_PASSPHRASE ||
(*vmk)->protection == BITLK_PROTECTION_STARTUP_KEY;
- while (end - start > 2) {
+ while ((end - start) >= (ssize_t)(sizeof(key_entry_size) + sizeof(key_entry_type) + sizeof(key_entry_value))) {
/* size of this entry */
memcpy(&key_entry_size, data + start, sizeof(key_entry_size));
key_entry_size = le16_to_cpu(key_entry_size);
if (key_entry_size == 0)
break;
+ if (key_entry_size > (end - start))
+ return -EINVAL;
+
/* type and value of this entry */
memcpy(&key_entry_type, data + start + sizeof(key_entry_size), sizeof(key_entry_type));
memcpy(&key_entry_value,
}
/* stretch key with salt, skip 4 B (encryption method of the stretch key) */
- if (key_entry_value == BITLK_ENTRY_VALUE_STRETCH_KEY)
+ if (key_entry_value == BITLK_ENTRY_VALUE_STRETCH_KEY) {
+ if ((end - start) < (BITLK_ENTRY_HEADER_LEN + BITLK_SALT_SIZE + 4))
+ return -EINVAL;
memcpy((*vmk)->salt,
data + start + BITLK_ENTRY_HEADER_LEN + 4,
- sizeof((*vmk)->salt));
+ BITLK_SALT_SIZE);
/* AES-CCM encrypted key */
- else if (key_entry_value == BITLK_ENTRY_VALUE_ENCRYPTED_KEY) {
+ } else if (key_entry_value == BITLK_ENTRY_VALUE_ENCRYPTED_KEY) {
+ if (key_entry_size < (BITLK_ENTRY_HEADER_LEN + BITLK_NONCE_SIZE + BITLK_VMK_MAC_TAG_SIZE))
+ return -EINVAL;
/* nonce */
memcpy((*vmk)->nonce,
data + start + BITLK_ENTRY_HEADER_LEN,
- sizeof((*vmk)->nonce));
+ BITLK_NONCE_SIZE);
/* MAC tag */
memcpy((*vmk)->mac_tag,
data + start + BITLK_ENTRY_HEADER_LEN + BITLK_NONCE_SIZE,
- sizeof((*vmk)->mac_tag));
+ BITLK_VMK_MAC_TAG_SIZE);
/* 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;
} else if (key_entry_value == BITLK_ENTRY_VALUE_RECOVERY_TIME) {
;
} else if (key_entry_value == BITLK_ENTRY_VALUE_STRING) {
- if (convert_to_utf8(cd, data + start + BITLK_ENTRY_HEADER_LEN, key_entry_size - BITLK_ENTRY_HEADER_LEN, &string) < 0) {
- log_err(cd, _("Invalid string found when parsing Volume Master Key."));
+ if (key_entry_size < BITLK_ENTRY_HEADER_LEN)
+ return -EINVAL;
+ string = malloc((key_entry_size - BITLK_ENTRY_HEADER_LEN) * 2 + 1);
+ if (!string)
+ return -ENOMEM;
+ r = crypt_utf16_to_utf8(&string, CONST_CAST(char16_t *)(data + start + BITLK_ENTRY_HEADER_LEN),
+ key_entry_size - BITLK_ENTRY_HEADER_LEN);
+ if (r < 0 || !string) {
free(string);
+ log_err(cd, _("Invalid string found when parsing Volume Master Key."));
return -EINVAL;
} else if ((*vmk)->name != NULL) {
if (supported) {
struct bitlk_fve_metadata fve = {};
struct bitlk_entry_vmk entry_vmk = {};
uint8_t *fve_entries = NULL;
+ size_t fve_entries_size = 0;
uint32_t fve_metadata_size = 0;
int fve_offset = 0;
char guid_buf[UUID_STR_LEN] = {0};
int i = 0;
int r = 0;
int start = 0;
- int end = 0;
size_t key_size = 0;
const char *key = NULL;
+ char *description = NULL;
struct bitlk_vmk *vmk = NULL;
struct bitlk_vmk *vmk_p = params->vmks;
/* read and check the 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 BITLK signature from %s."), device_path(device));
- r = -EINVAL;
+ log_dbg(cd, "Failed to read BITLK signature from %s.", device_path(device));
+ r = -EIO;
goto out;
}
params->togo = true;
fve_offset = BITLK_HEADER_METADATA_OFFSET_TOGO;
} else {
- log_err(cd, _("Invalid or unknown signature for BITLK device."));
+ log_dbg(cd, "Invalid or unknown signature for BITLK device.");
r = -EINVAL;
goto out;
}
le16_to_cpu(fve.curr_state), le16_to_cpu(fve.next_state));
}
+ params->volume_size = le64_to_cpu(fve.volume_size);
params->metadata_version = le16_to_cpu(fve.fve_version);
- fve_metadata_size = le32_to_cpu(fve.metadata_size);
switch (le16_to_cpu(fve.encryption)) {
/* AES-CBC with Elephant difuser */
params->creation_time = filetime_to_unixtime(le64_to_cpu(fve.creation_time));
+ fve_metadata_size = le32_to_cpu(fve.metadata_size);
+ if (fve_metadata_size < (BITLK_FVE_METADATA_HEADER_LEN + sizeof(entry_size) + sizeof(entry_type)) ||
+ fve_metadata_size > BITLK_FVE_METADATA_SIZE) {
+ r = -EINVAL;
+ goto out;
+ }
+ fve_entries_size = fve_metadata_size - BITLK_FVE_METADATA_HEADER_LEN;
+
/* read and parse all FVE metadata entries */
- fve_entries = malloc(fve_metadata_size - BITLK_FVE_METADATA_HEADER_LEN);
+ fve_entries = malloc(fve_entries_size);
if (!fve_entries) {
r = -ENOMEM;
goto out;
}
- memset(fve_entries, 0, (fve_metadata_size - BITLK_FVE_METADATA_HEADER_LEN));
+ memset(fve_entries, 0, fve_entries_size);
- log_dbg(cd, "Reading BITLK FVE metadata entries of size %" PRIu32 " on device %s, offset %" PRIu64 ".",
- fve_metadata_size - BITLK_FVE_METADATA_HEADER_LEN, device_path(device),
- params->metadata_offset[0] + BITLK_FVE_METADATA_HEADERS_LEN);
+ log_dbg(cd, "Reading BITLK FVE metadata entries of size %zu on device %s, offset %" PRIu64 ".",
+ fve_entries_size, device_path(device), params->metadata_offset[0] + BITLK_FVE_METADATA_HEADERS_LEN);
if (read_lseek_blockwise(devfd, device_block_size(cd, device),
- device_alignment(device), fve_entries, fve_metadata_size - BITLK_FVE_METADATA_HEADER_LEN,
- params->metadata_offset[0] + BITLK_FVE_METADATA_HEADERS_LEN) != (ssize_t)(fve_metadata_size - BITLK_FVE_METADATA_HEADER_LEN)) {
+ device_alignment(device), fve_entries, fve_entries_size,
+ params->metadata_offset[0] + BITLK_FVE_METADATA_HEADERS_LEN) != (ssize_t)fve_entries_size) {
log_err(cd, _("Failed to read BITLK metadata entries from %s."), device_path(device));
r = -EINVAL;
goto out;
}
- end = fve_metadata_size - BITLK_FVE_METADATA_HEADER_LEN;
- while (end - start > 2) {
+ while ((fve_entries_size - start) >= (sizeof(entry_size) + sizeof(entry_type))) {
+
/* size of this entry */
memcpy(&entry_size, fve_entries + start, sizeof(entry_size));
entry_size = le16_to_cpu(entry_size);
if (entry_size == 0)
break;
+ if (entry_size > (fve_entries_size - start)) {
+ r = -EINVAL;
+ goto out;
+ }
+
/* type of this entry */
memcpy(&entry_type, fve_entries + start + sizeof(entry_size), sizeof(entry_type));
entry_type = le16_to_cpu(entry_type);
/* VMK */
if (entry_type == BITLK_ENTRY_TYPE_VMK) {
+ if (entry_size < (BITLK_ENTRY_HEADER_LEN + sizeof(entry_vmk))) {
+ r = -EINVAL;
+ goto out;
+ }
/* skip first four variables in the entry (entry size, type, value and version) */
memcpy(&entry_vmk,
fve_entries + start + BITLK_ENTRY_HEADER_LEN,
vmk_p = vmk;
vmk = vmk->next;
/* FVEK */
- } else if (entry_type == BITLK_ENTRY_TYPE_FVEK) {
+ } else if (entry_type == BITLK_ENTRY_TYPE_FVEK && !params->fvek) {
+ if (entry_size < (BITLK_ENTRY_HEADER_LEN + BITLK_NONCE_SIZE + BITLK_VMK_MAC_TAG_SIZE)) {
+ r = -EINVAL;
+ goto out;
+ }
params->fvek = malloc(sizeof(struct bitlk_fvek));
if (!params->fvek) {
r = -ENOMEM;
}
memcpy(params->fvek->nonce,
fve_entries + start + BITLK_ENTRY_HEADER_LEN,
- sizeof(params->fvek->nonce));
+ BITLK_NONCE_SIZE);
/* MAC tag */
memcpy(params->fvek->mac_tag,
fve_entries + start + BITLK_ENTRY_HEADER_LEN + BITLK_NONCE_SIZE,
- sizeof(params->fvek->mac_tag));
+ BITLK_VMK_MAC_TAG_SIZE);
/* 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;
/* volume header info (location and size) */
} else if (entry_type == BITLK_ENTRY_TYPE_VOLUME_HEADER) {
struct bitlk_entry_header_block entry_header;
+ if ((fve_entries_size - start) < (BITLK_ENTRY_HEADER_LEN + sizeof(entry_header))) {
+ r = -EINVAL;
+ goto out;
+ }
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));
+ } else if (entry_type == BITLK_ENTRY_TYPE_DESCRIPTION && !params->description) {
+ if (entry_size < BITLK_ENTRY_HEADER_LEN) {
+ r = -EINVAL;
+ goto out;
+ }
+ description = malloc((entry_size - BITLK_ENTRY_HEADER_LEN) * 2 + 1);
+ if (!description) {
+ r = -ENOMEM;
+ goto out;
+ }
+ r = crypt_utf16_to_utf8(&description, CONST_CAST(char16_t *)(fve_entries + start + BITLK_ENTRY_HEADER_LEN),
+ entry_size - BITLK_ENTRY_HEADER_LEN);
if (r < 0) {
+ free(description);
BITLK_bitlk_vmk_free(vmk);
+ log_err(cd, _("Failed to convert BITLK volume description"));
goto out;
}
+ params->description = description;
}
start += entry_size;
log_std(cd, "Version: \t%u\n", params->metadata_version);
log_std(cd, "GUID: \t%s\n", params->guid);
log_std(cd, "Sector size: \t%u [bytes]\n", params->sector_size);
+ log_std(cd, "Volume size: \t%" PRIu64 " [bytes]\n", params->volume_size);
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, "\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, "");
+ crypt_log_hex(cd, (const char *) vmk_p->salt, 16, "", 0, NULL);
log_std(cd, "\n");
vk_p = vmk_p->vk;
- each part is a number dividable by 11
*/
if (passwordLen != BITLK_RECOVERY_KEY_LEN) {
- if (passwordLen == BITLK_RECOVERY_KEY_LEN + 1 && password[passwordLen - 1] == '\n') {
- /* looks like a recovery key with an extra newline, possibly from a key file */
- passwordLen--;
- log_dbg(cd, "Possible extra EOL stripped from the recovery key.");
- } else
- return 0;
- }
+ if (passwordLen == BITLK_RECOVERY_KEY_LEN + 1 && password[passwordLen - 1] == '\n') {
+ /* looks like a recovery key with an extra newline, possibly from a key file */
+ passwordLen--;
+ log_dbg(cd, "Possible extra EOL stripped from the recovery key.");
+ } else
+ return 0;
+ }
for (i = BITLK_RECOVERY_PART_LEN; i < passwordLen; i += BITLK_RECOVERY_PART_LEN + 1) {
if (password[i] != '-')
return 0;
}
-static int parse_external_key_entry(struct crypt_device *cd, const char *data, int start, int end, struct volume_key **vk)
+static int parse_external_key_entry(struct crypt_device *cd,
+ const char *data,
+ int start,
+ int end,
+ struct volume_key **vk,
+ const struct bitlk_metadata *params)
{
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 bitlk_guid guid;
+ char guid_buf[UUID_STR_LEN] = {0};
- while (end - start > 2) {
+ while ((end - start) >= (ssize_t)(sizeof(key_entry_size) + sizeof(key_entry_type) + sizeof(key_entry_value))) {
/* size of this entry */
memcpy(&key_entry_size, data + start, sizeof(key_entry_size));
key_entry_size = le16_to_cpu(key_entry_size);
if (key_entry_size == 0)
break;
+ if (key_entry_size > (end - start))
+ return -EINVAL;
+
/* type and value of this entry */
memcpy(&key_entry_type, data + start + sizeof(key_entry_size), sizeof(key_entry_type));
memcpy(&key_entry_value,
key_entry_type = le16_to_cpu(key_entry_type);
key_entry_value = le16_to_cpu(key_entry_value);
- /* only properties should be in this entry */
- if (key_entry_type != BITLK_ENTRY_TYPE_PROPERTY) {
+ if (key_entry_type != BITLK_ENTRY_TYPE_PROPERTY && key_entry_type != BITLK_ENTRY_TYPE_VOLUME_GUID) {
log_err(cd, _("Unexpected metadata entry type '%u' found when parsing external key."), key_entry_type);
return -EINVAL;
}
if (key_entry_value == BITLK_ENTRY_VALUE_KEY) {
+ if (key_entry_size < (BITLK_ENTRY_HEADER_LEN + 4))
+ return -EINVAL;
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);
/* optional "ExternalKey" string, we can safely ignore it */
} else if (key_entry_value == BITLK_ENTRY_VALUE_STRING)
;
- else {
+ /* GUID of the BitLocker device we are trying to open with this key */
+ else if (key_entry_value == BITLK_ENTRY_VALUE_GUID) {
+ if ((end - start) < (ssize_t)(BITLK_ENTRY_HEADER_LEN + sizeof(struct bitlk_guid)))
+ return -EINVAL;
+ memcpy(&guid, data + start + BITLK_ENTRY_HEADER_LEN, sizeof(struct bitlk_guid));
+ guid_to_string(&guid, guid_buf);
+ if (strcmp(guid_buf, params->guid) != 0) {
+ log_err(cd, _("BEK file GUID '%s' does not match GUID of the volume."), guid_buf);
+ return -EINVAL;
+ }
+ } else {
log_err(cd, _("Unexpected metadata entry value '%u' found when parsing external key."), key_entry_value);
return -EINVAL;
}
const char *password,
size_t passwordLen,
const struct bitlk_vmk *vmk,
- struct volume_key **su_key)
+ struct volume_key **su_key,
+ const struct bitlk_metadata *params)
{
struct bitlk_bek_header bek_header = {0};
char guid_buf[UUID_STR_LEN] = {0};
uint16_t key_entry_type = 0;
uint16_t key_entry_value = 0;
- if (passwordLen < BITLK_BEK_FILE_HEADER_LEN)
+ if (passwordLen < (BITLK_BEK_FILE_HEADER_LEN + sizeof(key_entry_size) + sizeof(key_entry_type) + sizeof(key_entry_value)))
return -EPERM;
memcpy(&bek_header, password, BITLK_BEK_FILE_HEADER_LEN);
else
return -EPERM;
- if (bek_header.metadata_version != 1) {
- log_err(cd, "Unsupported BEK metadata version %" PRIu32 "", bek_header.metadata_version);
+ if (le32_to_cpu(bek_header.metadata_version) != 1) {
+ log_err(cd, _("Unsupported BEK metadata version %" PRIu32), le32_to_cpu(bek_header.metadata_version));
return -ENOTSUP;
}
- if (bek_header.metadata_size != passwordLen) {
- log_err(cd, "Unexpected BEK metadata size %" PRIu32 " does not match BEK file length", bek_header.metadata_size);
+ if (le32_to_cpu(bek_header.metadata_size) != passwordLen) {
+ log_err(cd, _("Unexpected BEK metadata size %" PRIu32 " does not match BEK file length"),
+ le32_to_cpu(bek_header.metadata_size));
return -EINVAL;
}
if (key_entry_type == BITLK_ENTRY_TYPE_STARTUP_KEY && key_entry_value == BITLK_ENTRY_VALUE_EXTERNAL_KEY) {
return parse_external_key_entry(cd, password,
BITLK_BEK_FILE_HEADER_LEN + BITLK_ENTRY_HEADER_LEN + BITLK_STARTUP_KEY_HEADER_LEN,
- passwordLen, su_key);
+ passwordLen, su_key, params);
} else {
log_err(cd, _("Unexpected metadata entry found when parsing startup key."));
log_dbg(cd, "Entry type: %u, entry value: %u", key_entry_type, key_entry_value);
struct bitlk_kdf_data kdf = {};
struct crypt_hash *hd = NULL;
int len = 0;
- char *utf16Password = NULL;
+ char16_t *utf16Password = NULL;
int i = 0;
int r = 0;
if (!recovery) {
/* passphrase: convert to UTF-16 first, then sha256(sha256(pw)) */
- r = passphrase_to_utf16(cd, CONST_CAST(char*)password, passwordLen, &utf16Password);
+ utf16Password = crypt_safe_alloc(sizeof(char16_t) * (passwordLen + 1));
+ if (!utf16Password) {
+ r = -ENOMEM;
+ goto out;
+ }
+ r = crypt_utf8_to_utf16(&utf16Password, CONST_CAST(char*)password, passwordLen);
if (r < 0)
goto out;
- crypt_hash_write(hd, utf16Password, passwordLen * 2);
+ crypt_hash_write(hd, (char*)utf16Password, passwordLen * 2);
r = crypt_hash_final(hd, kdf.initial_sha256, len);
if (r < 0)
goto out;
return r;
}
-int BITLK_activate(struct crypt_device *cd,
- const char *name,
- const char *password,
- size_t passwordLen,
- const struct bitlk_metadata *params,
- uint32_t flags)
+int BITLK_get_volume_key(struct crypt_device *cd,
+ const char *password,
+ size_t passwordLen,
+ const struct bitlk_metadata *params,
+ struct volume_key **open_fvek_key)
{
int r = 0;
- int i = 0;
- int j = 0;
- int min = 0;
- int num_segments = 0;
- struct crypt_dm_active_device dmd = {
- .flags = flags,
- };
- struct dm_target *next_segment = NULL;
struct volume_key *open_vmk_key = NULL;
- struct volume_key *open_fvek_key = NULL;
struct volume_key *vmk_dec_key = NULL;
struct volume_key *recovery_key = NULL;
const struct bitlk_vmk *next_vmk = NULL;
- struct segment segments[MAX_BITLK_SEGMENTS] = {};
- struct segment temp;
- uint64_t next_start = 0;
- uint64_t next_end = 0;
- uint64_t last_segment = 0;
- uint32_t dmt_flags;
-
- if (!params->state) {
- log_err(cd, _("This BITLK device is in an unsupported state and cannot be activated."));
- r = -ENOTSUP;
- goto out;
- }
-
- if (params->type != BITLK_ENCRYPTION_TYPE_NORMAL) {
- log_err(cd, _("BITLK devices with type '%s' cannot be activated."), get_bitlk_type_string(params->type));
- r = -ENOTSUP;
- goto out;
- }
next_vmk = params->vmks;
while (next_vmk) {
if (r)
return r;
} else if (next_vmk->protection == BITLK_PROTECTION_STARTUP_KEY) {
- r = get_startup_key(cd, password, passwordLen, next_vmk, &vmk_dec_key);
+ r = get_startup_key(cd, password, passwordLen, next_vmk, &vmk_dec_key, params);
if (r) {
next_vmk = next_vmk->next;
continue;
}
crypt_free_volume_key(vmk_dec_key);
- r = decrypt_key(cd, &open_fvek_key, params->fvek->vk, open_vmk_key,
+ r = decrypt_key(cd, open_fvek_key, params->fvek->vk, open_vmk_key,
params->fvek->mac_tag, BITLK_VMK_MAC_TAG_SIZE,
params->fvek->nonce, BITLK_NONCE_SIZE, true);
if (r < 0) {
return r;
}
- /* Password verify only */
- if (!name) {
- crypt_free_volume_key(open_fvek_key);
- return r;
+ return 0;
+}
+
+static int _activate_check(struct crypt_device *cd,
+ const struct bitlk_metadata *params)
+{
+ const struct bitlk_vmk *next_vmk = NULL;
+
+ if (!params->state) {
+ log_err(cd, _("This BITLK device is in an unsupported state and cannot be activated."));
+ return -ENOTSUP;
+ }
+
+ if (params->type != BITLK_ENCRYPTION_TYPE_NORMAL) {
+ log_err(cd, _("BITLK devices with type '%s' cannot be activated."), get_bitlk_type_string(params->type));
+ return -ENOTSUP;
}
next_vmk = params->vmks;
while (next_vmk) {
if (next_vmk->protection == BITLK_PROTECTION_CLEAR_KEY) {
- crypt_free_volume_key(open_fvek_key);
log_err(cd, _("Activation of partially decrypted BITLK device is not supported."));
return -ENOTSUP;
}
next_vmk = next_vmk->next;
}
+ return 0;
+}
+
+static int _activate(struct crypt_device *cd,
+ const char *name,
+ struct volume_key *open_fvek_key,
+ const struct bitlk_metadata *params,
+ uint32_t flags)
+{
+ int r = 0;
+ int i = 0;
+ int j = 0;
+ int min = 0;
+ int num_segments = 0;
+ struct crypt_dm_active_device dmd = {
+ .flags = flags,
+ };
+ struct dm_target *next_segment = NULL;
+ struct segment segments[MAX_BITLK_SEGMENTS] = {};
+ struct segment temp;
+ uint64_t next_start = 0;
+ uint64_t next_end = 0;
+ uint64_t last_segment = 0;
+ uint32_t dmt_flags = 0;
+
+ r = _activate_check(cd, params);
+ if (r)
+ return r;
+
r = device_block_adjust(cd, crypt_data_device(cd), DEV_EXCL,
0, &dmd.size, &dmd.flags);
- if (r) {
- crypt_free_volume_key(open_fvek_key);
+ if (r)
return r;
- }
+
+ if (dmd.size * SECTOR_SIZE != params->volume_size)
+ log_std(cd, _("WARNING: BitLocker volume size %" PRIu64 " does not match the underlying device size %" PRIu64 ""),
+ params->volume_size,
+ dmd.size * SECTOR_SIZE);
/* there will be always 4 dm-zero segments: 3x metadata, 1x FS header */
for (i = 0; i < 3; i++) {
log_err(cd, _("Cannot activate device, kernel dm-crypt is missing support for BITLK Elephant diffuser."));
r = -ENOTSUP;
}
+ if ((dmd.flags & CRYPT_ACTIVATE_IV_LARGE_SECTORS) && !(dmt_flags & DM_SECTOR_SIZE_SUPPORTED)) {
+ log_err(cd, _("Cannot activate device, kernel dm-crypt is missing support for large sector size."));
+ r = -ENOTSUP;
+ }
+ if (dm_flags(cd, DM_ZERO, &dmt_flags) < 0) {
+ log_err(cd, _("Cannot activate device, kernel dm-zero module is missing."));
+ r = -ENOTSUP;
+ }
}
out:
dm_targets_free(cd, &dmd);
+ return r;
+}
+
+int BITLK_activate_by_passphrase(struct crypt_device *cd,
+ const char *name,
+ const char *password,
+ size_t passwordLen,
+ const struct bitlk_metadata *params,
+ uint32_t flags)
+{
+ int r = 0;
+ struct volume_key *open_fvek_key = NULL;
+
+ r = _activate_check(cd, params);
+ if (r)
+ return r;
+
+ r = BITLK_get_volume_key(cd, password, passwordLen, params, &open_fvek_key);
+ if (r < 0)
+ goto out;
+
+ /* Password verify only */
+ if (!name)
+ goto out;
+
+ r = _activate(cd, name, open_fvek_key, params, flags);
+out:
+ crypt_free_volume_key(open_fvek_key);
+ return r;
+}
+
+int BITLK_activate_by_volume_key(struct crypt_device *cd,
+ const char *name,
+ const char *volume_key,
+ size_t volume_key_size,
+ const struct bitlk_metadata *params,
+ uint32_t flags)
+{
+ int r = 0;
+ struct volume_key *open_fvek_key = NULL;
+
+ r = _activate_check(cd, params);
+ if (r)
+ return r;
+
+ open_fvek_key = crypt_alloc_volume_key(volume_key_size, volume_key);
+ if (!open_fvek_key)
+ return -ENOMEM;
+
+ r = _activate(cd, name, open_fvek_key, params, flags);
+
crypt_free_volume_key(open_fvek_key);
return r;
}