Add backend support for new device-mapper kernel options.

This patch adds support for using keyring for volume key
and support for new integrity fields for dm-crypt.

Also helpers for searching disk by id.

To be used later.
This commit is contained in:
Milan Broz
2017-09-24 12:00:05 +02:00
parent 894e7b9357
commit c56bdee177
14 changed files with 606 additions and 37 deletions

View File

@@ -35,6 +35,8 @@
#include "internal.h"
#define DM_UUID_LEN 129
#define DM_BY_ID_PREFIX "dm-uuid-"
#define DM_BY_ID_PREFIX_LEN 8
#define DM_UUID_PREFIX "CRYPT-"
#define DM_UUID_PREFIX_LEN 6
#define DM_CRYPT_TARGET "crypt"
@@ -382,36 +384,179 @@ static size_t get_key_size_strlen(size_t key_size)
return ret;
}
#define CLEN 64 /* 2*MAX_CIPHER_LEN */
#define CLENS "63" /* for sscanf length + '\0' */
#define CAPIL 144 /* should be enough to fit whole capi string */
#define CAPIS "143" /* for sscanf of crypto API string + 16 + \0 */
static int cipher_c2dm(const char *org_c, const char *org_i, unsigned tag_size,
char *c_dm, int c_dm_size,
char *i_dm, int i_dm_size)
{
int c_size = 0, i_size = 0, i;
char cipher[CLEN], mode[CLEN], iv[CLEN], tmp[CLEN];
char capi[CAPIL];
if (!c_dm || !c_dm_size || !i_dm || !i_dm_size)
return -EINVAL;
i = sscanf(org_c, "%" CLENS "[^-]-%" CLENS "s", cipher, tmp);
if (i != 2)
return -EINVAL;
i = sscanf(tmp, "%" CLENS "[^-]-%" CLENS "s", mode, iv);
if (i == 1) {
strncpy(iv, mode, CLEN);
*mode = '\0';
if (snprintf(capi, sizeof(capi), "%s", cipher) < 0)
return -EINVAL;
} else if (i == 2) {
if (snprintf(capi, sizeof(capi), "%s(%s)", mode, cipher) < 0)
return -EINVAL;
} else
return -EINVAL;
if (!org_i) {
/* legacy mode: CIPHER-MODE-IV*/
i_size = snprintf(i_dm, i_dm_size, "%s", "");
c_size = snprintf(c_dm, c_dm_size, "%s", org_c);
} else if (!strcmp(org_i, "none")) {
/* IV only: capi:MODE(CIPHER)-IV */
i_size = snprintf(i_dm, i_dm_size, " integrity:%u:none", tag_size);
c_size = snprintf(c_dm, c_dm_size, "capi:%s-%s", capi, iv);
} else if (!strcmp(org_i, "aead") && !strcmp(mode, "ccm")) {
/* CCM AEAD: capi:rfc4309(MODE(CIPHER))-IV */
i_size = snprintf(i_dm, i_dm_size, " integrity:%u:aead", tag_size);
c_size = snprintf(c_dm, c_dm_size, "capi:rfc4309(%s)-%s", capi, iv);
} else if (!strcmp(org_i, "aead")) {
/* AEAD: capi:MODE(CIPHER))-IV */
i_size = snprintf(i_dm, i_dm_size, " integrity:%u:aead", tag_size);
c_size = snprintf(c_dm, c_dm_size, "capi:%s-%s", capi, iv);
} else if (!strcmp(org_i, "poly1305")) {
/* POLY1305 AEAD: capi:rfc7539(MODE(CIPHER),POLY1305)-IV */
i_size = snprintf(i_dm, i_dm_size, " integrity:%u:aead", tag_size);
c_size = snprintf(c_dm, c_dm_size, "capi:rfc7539(%s,poly1305)-%s", capi, iv);
} else {
/* other AEAD: capi:authenc(<AUTH>,MODE(CIPHER))-IV */
i_size = snprintf(i_dm, i_dm_size, " integrity:%u:aead", tag_size);
c_size = snprintf(c_dm, c_dm_size, "capi:authenc(%s,%s)-%s", org_i, capi, iv);
}
if (c_size < 0 || c_size == c_dm_size)
return -EINVAL;
if (i_size < 0 || i_size == i_dm_size)
return -EINVAL;
return 0;
}
static int cipher_dm2c(char **org_c, char **org_i, const char *c_dm, const char *i_dm)
{
char cipher[CLEN], mode[CLEN], iv[CLEN], auth[CLEN];
char tmp[CAPIL], capi[CAPIL];
size_t len;
int i;
if (!c_dm)
return -EINVAL;
/* legacy mode */
if (strncmp(c_dm, "capi:", 4)) {
if (!(*org_c = strdup(c_dm)))
return -ENOMEM;
*org_i = NULL;
return 0;
}
/* modes with capi: prefix */
i = sscanf(c_dm, "capi:%" CAPIS "[^-]-%" CLENS "s", tmp, iv);
if (i != 2)
return -EINVAL;
len = strlen(tmp);
if (len < 2)
return -EINVAL;
if (tmp[len-1] == ')')
tmp[len-1] = '\0';
if (sscanf(tmp, "rfc4309(%" CAPIS "s", capi) == 1) {
if (!(*org_i = strdup("aead")))
return -ENOMEM;
} else if (sscanf(tmp, "rfc7539(%" CAPIS "[^,],%" CLENS "s", capi, auth) == 2) {
if (!(*org_i = strdup(auth)))
return -ENOMEM;
} else if (sscanf(tmp, "authenc(%" CLENS "[^,],%" CAPIS "s", auth, capi) == 2) {
if (!(*org_i = strdup(auth)))
return -ENOMEM;
} else {
if (i_dm) {
if (!(*org_i = strdup(i_dm)))
return -ENOMEM;
} else
*org_i = NULL;
strncpy(capi, tmp, sizeof(capi));
}
i = sscanf(capi, "%" CLENS "[^(](%" CLENS "[^)])", mode, cipher);
if (i == 2)
snprintf(tmp, sizeof(tmp), "%s-%s-%s", cipher, mode, iv);
else
snprintf(tmp, sizeof(tmp), "%s-%s", capi, iv);
if (!(*org_c = strdup(tmp))) {
free(*org_i);
*org_i = NULL;
return -ENOMEM;
}
return 0;
}
/* https://gitlab.com/cryptsetup/cryptsetup/wikis/DMCrypt */
static char *get_dm_crypt_params(struct crypt_dm_active_device *dmd, uint32_t flags)
{
int r, max_size, null_cipher = 0, num_options = 0, keystr_len = 0;
char *params, *hexkey;
char features[256];
char sector_feature[32], features[256], integrity_dm[256], cipher_dm[256];
if (!dmd)
return NULL;
r = cipher_c2dm(dmd->u.crypt.cipher, dmd->u.crypt.integrity, dmd->u.crypt.tag_size,
cipher_dm, sizeof(cipher_dm), integrity_dm, sizeof(integrity_dm));
if (r < 0)
return NULL;
if (flags & CRYPT_ACTIVATE_ALLOW_DISCARDS)
num_options++;
if (flags & CRYPT_ACTIVATE_SAME_CPU_CRYPT)
num_options++;
if (flags & CRYPT_ACTIVATE_SUBMIT_FROM_CRYPT_CPUS)
num_options++;
if (dmd->u.crypt.integrity)
num_options++;
if (num_options)
snprintf(features, sizeof(features)-1, " %d%s%s%s", num_options,
if (dmd->u.crypt.sector_size != SECTOR_SIZE) {
num_options++;
snprintf(sector_feature, sizeof(sector_feature), " sector_size:%u", dmd->u.crypt.sector_size);
} else
*sector_feature = '\0';
if (num_options) {
snprintf(features, sizeof(features)-1, " %d%s%s%s%s%s", num_options,
(flags & CRYPT_ACTIVATE_ALLOW_DISCARDS) ? " allow_discards" : "",
(flags & CRYPT_ACTIVATE_SAME_CPU_CRYPT) ? " same_cpu_crypt" : "",
(flags & CRYPT_ACTIVATE_SUBMIT_FROM_CRYPT_CPUS) ? " submit_from_crypt_cpus" : "");
else
(flags & CRYPT_ACTIVATE_SUBMIT_FROM_CRYPT_CPUS) ? " submit_from_crypt_cpus" : "",
sector_feature, integrity_dm);
} else
*features = '\0';
if (!strncmp(dmd->u.crypt.cipher, "cipher_null-", 12))
if (!strncmp(cipher_dm, "cipher_null-", 12))
null_cipher = 1;
if (dmd->u.crypt.key_in_keyring) {
keystr_len = strlen(dmd->u.crypt.key_description) + get_key_size_strlen(dmd->u.crypt.vk->keylength) + 9;
if (flags & CRYPT_ACTIVATE_KEYRING_KEY) {
keystr_len = strlen(dmd->u.crypt.vk->key_description) + get_key_size_strlen(dmd->u.crypt.vk->keylength) + 9;
hexkey = crypt_safe_alloc(keystr_len);
} else
hexkey = crypt_safe_alloc(null_cipher ? 2 : (dmd->u.crypt.vk->keylength * 2 + 1));
@@ -421,8 +566,8 @@ static char *get_dm_crypt_params(struct crypt_dm_active_device *dmd, uint32_t fl
if (null_cipher)
strncpy(hexkey, "-", 2);
else if (dmd->u.crypt.key_in_keyring) {
r = snprintf(hexkey, keystr_len, ":%zu:logon:%s", dmd->u.crypt.vk->keylength, dmd->u.crypt.key_description);
else if (flags & CRYPT_ACTIVATE_KEYRING_KEY) {
r = snprintf(hexkey, keystr_len, ":%zu:logon:%s", dmd->u.crypt.vk->keylength, dmd->u.crypt.vk->key_description);
if (r < 0 || r >= keystr_len) {
params = NULL;
goto out;
@@ -430,7 +575,7 @@ static char *get_dm_crypt_params(struct crypt_dm_active_device *dmd, uint32_t fl
} else
hex_key(hexkey, dmd->u.crypt.vk->keylength, dmd->u.crypt.vk->key);
max_size = strlen(hexkey) + strlen(dmd->u.crypt.cipher) +
max_size = strlen(hexkey) + strlen(cipher_dm) +
strlen(device_block_path(dmd->data_device)) +
strlen(features) + 64;
params = crypt_safe_alloc(max_size);
@@ -438,7 +583,7 @@ static char *get_dm_crypt_params(struct crypt_dm_active_device *dmd, uint32_t fl
goto out;
r = snprintf(params, max_size, "%s %s %" PRIu64 " %s %" PRIu64 "%s",
dmd->u.crypt.cipher, hexkey, dmd->u.crypt.iv_offset,
cipher_dm, hexkey, dmd->u.crypt.iv_offset,
device_block_path(dmd->data_device), dmd->u.crypt.offset,
features);
if (r < 0 || r >= max_size) {
@@ -852,6 +997,31 @@ static int dm_prepare_uuid(const char *name, const char *type, const char *uuid,
return 1;
}
int lookup_dm_dev_by_uuid(const char *uuid, const char *type)
{
int r;
char *c;
char dev_uuid[DM_UUID_LEN + DM_BY_ID_PREFIX_LEN] = DM_BY_ID_PREFIX;
if (!dm_prepare_uuid("", type, uuid, dev_uuid + DM_BY_ID_PREFIX_LEN, DM_UUID_LEN))
return -EINVAL;
c = strrchr(dev_uuid, '-');
if (!c)
return -EINVAL;
/* cut of dm name */
*c = '\0';
r = lookup_by_disk_id(dev_uuid);
if (r == -ENOENT) {
log_dbg("Search by disk id not available. Using sysfs instead.");
r = lookup_by_sysfs_uuid_field(dev_uuid + DM_BY_ID_PREFIX_LEN, DM_UUID_LEN);
}
return r;
}
static int _dm_create_device(const char *name, const char *type,
struct device *device, uint32_t flags,
const char *uuid, uint64_t size,
@@ -980,6 +1150,14 @@ static int check_retry(uint32_t *dmd_flags, uint32_t dmt_flags)
ret = 1;
}
/* If kernel keyring is not supported load key directly in dm-crypt */
if ((*dmd_flags & CRYPT_ACTIVATE_KEYRING_KEY) &&
!(dmt_flags & DM_KERNEL_KEYRING_SUPPORTED)) {
log_dbg("dm-crypt doesn't suport kernel keyring");
*dmd_flags = *dmd_flags & ~CRYPT_ACTIVATE_KEYRING_KEY;
ret = 1;
}
return ret;
}
@@ -1037,6 +1215,13 @@ int dm_create_device(struct crypt_device *cd, const char *name,
if (r == -EINVAL && dmd->target == DM_VERITY && dmd->u.verity.fec_device &&
!(dmt_flags & DM_VERITY_FEC_SUPPORTED))
log_err(cd, _("Requested dm-verity FEC options are not supported.\n"));
if (r == -EINVAL && dmd->target == DM_CRYPT) {
if (dmd->u.crypt.integrity && !(dmt_flags & DM_INTEGRITY_SUPPORTED))
log_err(cd, _("Requested data integrity options are not supported.\n"));
if (dmd->u.crypt.sector_size != SECTOR_SIZE && !(dmt_flags & DM_SECTOR_SIZE_SUPPORTED))
log_err(cd, _("Requested sector_size option is not supported.\n"));
}
out:
crypt_safe_free(table_params);
dm_exit_context();
@@ -1169,23 +1354,22 @@ static int _dm_query_crypt(uint32_t get_flags,
struct crypt_dm_active_device *dmd)
{
uint64_t val64;
char *rcipher, *key_, *rdevice, *endp, buffer[3], *arg;
unsigned int i;
char *rcipher, *rintegrity, *key_, *rdevice, *endp, buffer[3], *arg, *key_desc;
unsigned int i, val;
int r;
size_t key_size;
struct device *data_device = NULL;
char *cipher = NULL;
char *cipher = NULL, *integrity = NULL;
struct volume_key *vk = NULL;
memset(dmd, 0, sizeof(*dmd));
dmd->target = DM_CRYPT;
dmd->u.crypt.sector_size = SECTOR_SIZE;
r = -EINVAL;
rcipher = strsep(&params, " ");
/* cipher */
if (get_flags & DM_ACTIVE_CRYPT_CIPHER)
cipher = strdup(rcipher);
rintegrity = NULL;
/* skip */
key_ = strsep(&params, " ");
@@ -1216,6 +1400,8 @@ static int _dm_query_crypt(uint32_t get_flags,
val64 = strtoull(params, &params, 10);
dmd->u.crypt.offset = val64;
dmd->u.crypt.tag_size = 0;
/* Features section, available since crypt target version 1.11 */
if (*params) {
if (*params != ' ')
@@ -1238,7 +1424,14 @@ static int _dm_query_crypt(uint32_t get_flags,
dmd->flags |= CRYPT_ACTIVATE_SAME_CPU_CRYPT;
else if (!strcasecmp(arg, "submit_from_crypt_cpus"))
dmd->flags |= CRYPT_ACTIVATE_SUBMIT_FROM_CRYPT_CPUS;
else /* unknown option */
else if (sscanf(arg, "integrity:%u:", &val) == 1) {
dmd->u.crypt.tag_size = val;
rintegrity = strchr(arg + strlen("integrity:"), ':') + 1;
if (!rintegrity)
goto err;
} else if (sscanf(arg, "sector_size:%u", &val) == 1) {
dmd->u.crypt.sector_size = val;
} else /* unknown option */
goto err;
}
@@ -1247,6 +1440,17 @@ static int _dm_query_crypt(uint32_t get_flags,
goto err;
}
/* cipher */
if (get_flags & DM_ACTIVE_CRYPT_CIPHER) {
r = cipher_dm2c(CONST_CAST(char**)&cipher,
CONST_CAST(char**)&integrity,
rcipher, rintegrity);
if (r < 0)
goto err;
}
r = -EINVAL;
/* Never allow to return empty key */
if ((get_flags & DM_ACTIVE_CRYPT_KEY) && dmi->suspended) {
log_dbg("Cannot read volume key while suspended.");
@@ -1254,7 +1458,7 @@ static int _dm_query_crypt(uint32_t get_flags,
}
if (key_[0] == ':')
dmd->u.crypt.key_in_keyring = 1;
dmd->flags |= CRYPT_ACTIVATE_KEYRING_KEY;
if (get_flags & DM_ACTIVE_CRYPT_KEYSIZE) {
/* we will trust kernel the key_string is in expected format */
@@ -1272,11 +1476,12 @@ static int _dm_query_crypt(uint32_t get_flags,
if (get_flags & DM_ACTIVE_CRYPT_KEY) {
if (key_[0] == ':') {
dmd->u.crypt.key_description = strdup(strpbrk(strpbrk(key_ + 1, ":") + 1, ":") + 1);
if (!dmd->u.crypt.key_description) {
key_desc = strdup(strpbrk(strpbrk(key_ + 1, ":") + 1, ":") + 1);
if (!key_desc) {
r = -ENOMEM;
goto err;
}
crypt_volume_key_set_description(vk, key_desc);
} else {
buffer[2] = '\0';
for(i = 0; i < vk->keylength; i++) {
@@ -1294,6 +1499,8 @@ static int _dm_query_crypt(uint32_t get_flags,
if (cipher)
dmd->u.crypt.cipher = cipher;
if (integrity)
dmd->u.crypt.integrity = integrity;
if (data_device)
dmd->data_device = data_device;
if (vk)
@@ -1301,6 +1508,7 @@ static int _dm_query_crypt(uint32_t get_flags,
return 0;
err:
free(cipher);
free(integrity);
device_free(data_device);
crypt_free_volume_key(vk);
return r;
@@ -1543,9 +1751,9 @@ static int _dm_query_integrity(uint32_t get_flags,
uint32_t val32;
uint64_t val64;
char c, *str, *str2, *arg;
unsigned int features, val;
unsigned int i, features, val;
ssize_t len;
int i, r;
int r;
struct device *data_device = NULL;
char *integrity = NULL, *journal_crypt = NULL, *journal_integrity = NULL;
struct volume_key *vk = NULL;
@@ -1830,7 +2038,7 @@ out:
}
int dm_resume_and_reinstate_key(struct crypt_device *cd, const char *name,
size_t key_size, const char *key, unsigned key_in_keyring)
const struct volume_key *vk)
{
uint32_t dmt_flags;
int msg_size;
@@ -1843,10 +2051,10 @@ int dm_resume_and_reinstate_key(struct crypt_device *cd, const char *name,
if (!(dmt_flags & DM_KEY_WIPE_SUPPORTED))
goto out;
if (key_in_keyring)
msg_size = strlen(key) + get_key_size_strlen(key_size) + 17;
if (vk->key_description)
msg_size = strlen(vk->key_description) + get_key_size_strlen(vk->keylength) + 17;
else
msg_size = key_size * 2 + 10; // key set <key>
msg_size = vk->keylength * 2 + 10; // key set <key>
msg = crypt_safe_alloc(msg_size);
if (!msg) {
@@ -1855,10 +2063,10 @@ int dm_resume_and_reinstate_key(struct crypt_device *cd, const char *name,
}
strcpy(msg, "key set ");
if (key_in_keyring)
snprintf(msg + 8, msg_size - 8, ":%zu:logon:%s", key_size, key);
if (vk->key_description)
snprintf(msg + 8, msg_size - 8, ":%zu:logon:%s", vk->keylength, vk->key_description);
else
hex_key(&msg[8], key_size, key);
hex_key(&msg[8], vk->keylength, vk->key);
if (!_dm_message(name, msg, dmt_flags) ||
!_dm_simple(DM_DEVICE_RESUME, name, 1)) {