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

@@ -36,6 +36,7 @@
#include "utils_loop.h"
#include "utils_dm.h"
#include "utils_fips.h"
#include "utils_keyring.h"
#include "crypto_backend.h"
#include "libcryptsetup.h"
@@ -55,16 +56,21 @@
#define at_least(a, b) ({ __typeof__(a) __at_least = (a); (__at_least >= (b))?__at_least:(b); })
#define CRYPT_DEFAULT_SEGMENT 0
struct crypt_device;
struct volume_key {
size_t keylength;
const char *key_description;
char key[];
};
struct volume_key *crypt_alloc_volume_key(size_t keylength, const char *key);
struct volume_key *crypt_generate_volume_key(struct crypt_device *cd, size_t keylength);
void crypt_free_volume_key(struct volume_key *vk);
void crypt_volume_key_set_description(struct volume_key *key, const char *key_description);
const char *crypt_volume_key_get_description(const struct volume_key *key);
struct crypt_pbkdf_type *crypt_get_pbkdf(struct crypt_device *cd);
int init_pbkdf_type(struct crypt_device *cd,
@@ -120,6 +126,8 @@ int crypt_dev_is_partition(const char *dev_path);
char *crypt_get_partition_device(const char *dev_path, uint64_t offset, uint64_t size);
char *crypt_get_base_device(const char *dev_path);
uint64_t crypt_dev_partition_offset(const char *dev_path);
int lookup_by_disk_id(const char *dm_uuid);
int lookup_by_sysfs_uuid_field(const char *dm_uuid, size_t max_len);
ssize_t write_buffer(int fd, const void *buf, size_t count);
ssize_t read_buffer(int fd, void *buf, size_t count);
@@ -175,4 +183,7 @@ const char *crypt_get_integrity(struct crypt_device *cd);
int crypt_get_integrity_key_size(struct crypt_device *cd);
int crypt_get_integrity_tag_size(struct crypt_device *cd);
int crypt_key_in_keyring(struct crypt_device *cd);
void crypt_set_key_in_keyring(struct crypt_device *cd, unsigned key_in_keyring);
#endif /* INTERNAL_H */

View File

@@ -282,6 +282,24 @@ int crypt_memory_lock(struct crypt_device *cd, int lock);
* to on-disk format types
*/
/**
* Set or unset loading of volume keys via kernel keyring. When set to 'enabled'
* library loads key in kernel keyring first and pass the key description to
* dm-crypt instead of binary key copy. If set to 'disabled' library fall backs
* to classical method loading volume key directly in dm-crypt target.
*
* @param cd crypt device handle, can be @e NULL
* @param enable 0 to disable loading of volume keys via kernel keyring
* (classical method) otherwise enable it (default)
*
* @returns @e 0 on success or negative errno value otherwise.
*
* @note Currently loading of volume keys via kernel keyring is supported
* (and enabled by default) only for LUKS2 devices.
* @note The switch is global on the library level.
*/
int crypt_volume_key_keyring(struct crypt_device *cd, int enable);
/**
* @addtogroup crypt_type
* @{
@@ -527,6 +545,12 @@ int crypt_repair(struct crypt_device *cd,
* @param new_size - new device size in sectors or @e 0 to use all of the underlying device size
*
* @return @e 0 on success or negative errno value otherwise.
*
* @note Most notably it returns -EPERM when device was activated with volume key
* in kernel keyring and current device handle doesn't have verified key
* in kernel keyring in the same time. Perform any crypt_activate_*()
* operation with device name set to NULL to load verified volume key in
* the keyring.
*/
int crypt_resize(struct crypt_device *cd,
const char *name,
@@ -760,6 +784,8 @@ int crypt_keyslot_destroy(struct crypt_device *cd, int keyslot);
#define CRYPT_ACTIVATE_RESTART_ON_CORRUPTION (1 << 9)
/** dm-verity: ignore_zero_blocks - do not verify zero blocks */
#define CRYPT_ACTIVATE_IGNORE_ZERO_BLOCKS (1 << 10)
/** key loaded in kernel keyring instead directly in dm-crypt */
#define CRYPT_ACTIVATE_KEYRING_KEY (1 << 11)
/** dm-integrity: direct writes, do not use journal */
#define CRYPT_ACTIVATE_NO_JOURNAL (1 << 12)
/** dm-integrity: recovery mode - no journal, no integrity checks */
@@ -872,6 +898,29 @@ int crypt_activate_by_volume_key(struct crypt_device *cd,
/** force deactivation - if the device is busy, it is replaced by error device */
#define CRYPT_DEACTIVATE_FORCE (1 << 1)
/**
* Activate device using passphrase stored in kernel keyring.
*
*
* @param cd crypt device handle
* @param name name of device to create, if @e NULL only check passphrase in keyring
* @param key_description kernel keyring key description library should look
* for passphrase in
* @param keyslot requested keyslot to check or CRYPT_ANY_SLOT
* @param flags activation flags
*
* @return @e unlocked keyslot number on success or negative errno value otherwise.
*
* @note Keyslot passphrase must be stored in 'user' key type
* and the key has to be reachable for process context
* on behalf of which this function is called.
*/
int crypt_activate_by_keyring(struct crypt_device *cd,
const char *name,
const char *key_description,
int keyslot,
uint32_t flags);
/**
* Deactivate crypt device. This function tries to remove active device-mapper
* mapping from kernel. Also, sensitive data like the volume key are removed from

View File

@@ -31,10 +31,12 @@ CRYPTSETUP_2.0 {
crypt_activate_by_keyfile;
crypt_activate_by_keyfile_offset;
crypt_activate_by_volume_key;
crypt_activate_by_keyring;
crypt_deactivate;
crypt_deactivate_by_name;
crypt_volume_key_get;
crypt_volume_key_verify;
crypt_volume_key_keyring;
crypt_status;
crypt_dump;
crypt_benchmark;

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)) {

View File

@@ -216,6 +216,7 @@ int LOOPAES_activate(struct crypt_device *cd,
.vk = vk,
.offset = crypt_get_data_offset(cd),
.iv_offset = crypt_get_iv_offset(cd),
.sector_size = crypt_get_sector_size(cd),
}
};

View File

@@ -24,6 +24,7 @@
#include <fcntl.h>
#include <errno.h>
#include "luks.h"
#include "af.h"
#include "internal.h"
static void _error_hint(struct crypt_device *ctx, const char *device,
@@ -62,6 +63,7 @@ static int LUKS_endec_template(char *src, size_t srcLength,
.vk = vk,
.offset = sector,
.iv_offset = 0,
.sector_size = SECTOR_SIZE,
}
};
int r, devfd = -1;

View File

@@ -1183,6 +1183,7 @@ int LUKS1_activate(struct crypt_device *cd,
.vk = vk,
.offset = crypt_get_data_offset(cd),
.iv_offset = 0,
.sector_size = crypt_get_sector_size(cd),
}
};

View File

@@ -48,6 +48,7 @@ struct crypt_device {
struct crypt_pbkdf_type pbkdf;
/* global context scope settings */
unsigned key_in_keyring:1;
// FIXME: private binary headers and access it properly
// through sub-library (LUKS1, TCRYPT)
@@ -108,6 +109,12 @@ static int _crypto_logged = 0;
static void (*_default_log)(int level, const char *msg, void *usrptr) = NULL;
static int _debug_level = 0;
/* Library scope detection for kernel keyring support */
static int _kernel_keyring_supported;
/* Library allowed to use kernel keyring for loading VK in kernel crypto layer */
static int _vk_via_keyring = 1;
void crypt_set_debug_level(int level)
{
_debug_level = level;
@@ -124,6 +131,8 @@ void crypt_log(struct crypt_device *cd, int level, const char *msg)
cd->log(level, msg, cd->log_usrptr);
else if (_default_log)
_default_log(level, msg, NULL);
else if (_debug_level)
printf("%s", msg);
}
__attribute__((format(printf, 5, 6)))
@@ -400,6 +409,7 @@ int PLAIN_activate(struct crypt_device *cd,
.vk = vk,
.offset = crypt_get_data_offset(cd),
.iv_offset = crypt_get_iv_offset(cd),
.sector_size = crypt_get_sector_size(cd),
}
};
@@ -1514,9 +1524,36 @@ int crypt_repair(struct crypt_device *cd,
return r;
}
static int crypt_get_segment_key_description(struct crypt_device *cd, char **segment_key_desc, int segment)
{
char *key_desc;
int r;
size_t len;
if (!crypt_get_uuid(cd) || segment > 9 || segment < 0)
return -EINVAL;
len = strlen(crypt_get_uuid(cd)) + 14;
key_desc = malloc(len);
if (!key_desc)
return -ENOMEM;
r = snprintf(key_desc, len, "%s:%s-%u", "cryptsetup", crypt_get_uuid(cd), segment);
if (r < 0 || (size_t)r >= len) {
free(key_desc);
return -EINVAL;
}
*segment_key_desc = key_desc;
return 0;
}
int crypt_resize(struct crypt_device *cd, const char *name, uint64_t new_size)
{
struct crypt_dm_active_device dmd;
char *key_desc;
struct crypt_dm_active_device dmd = {};
int r;
/* Device context type must be initialised */
@@ -1538,6 +1575,20 @@ int crypt_resize(struct crypt_device *cd, const char *name, uint64_t new_size)
goto out;
}
if ((dmd.flags & CRYPT_ACTIVATE_KEYRING_KEY) && !crypt_key_in_keyring(cd)) {
r = -EPERM;
goto out;
}
if (crypt_key_in_keyring(cd)) {
r = crypt_get_segment_key_description(cd, &key_desc, CRYPT_DEFAULT_SEGMENT);
if (r)
goto out;
crypt_volume_key_set_description(dmd.u.crypt.vk, key_desc);
dmd.flags |= CRYPT_ACTIVATE_KEYRING_KEY;
}
if (crypt_loop_device(crypt_get_device_name(cd))) {
log_dbg("Trying to resize underlying loop device %s.",
crypt_get_device_name(cd));
@@ -1690,6 +1741,56 @@ void crypt_free(struct crypt_device *cd)
}
}
/* internal only */
int crypt_key_in_keyring(struct crypt_device *cd)
{
return cd ? cd->key_in_keyring : 0;
}
/* internal only */
void crypt_set_key_in_keyring(struct crypt_device *cd, unsigned key_in_keyring)
{
if (!cd)
return;
cd->key_in_keyring = key_in_keyring;
}
static void crypt_drop_keyring_key(struct crypt_device *cd, const char *active_key_desc)
{
char *seg_key_desc;
/* drop key detected in active dm-crypt table */
if (active_key_desc)
keyring_revoke_and_unlink_key(active_key_desc);
if (!crypt_key_in_keyring(cd) ||
crypt_get_segment_key_description(cd, &seg_key_desc, CRYPT_DEFAULT_SEGMENT))
return;
/* drop segment key */
if (!keyring_revoke_and_unlink_key(seg_key_desc))
crypt_set_key_in_keyring(cd, 0);
free(seg_key_desc);
}
static char *crypt_get_device_key_description(const char *name)
{
char *tmp = NULL;
struct crypt_dm_active_device dmd;
if (dm_query_device(NULL, name, DM_ACTIVE_CRYPT_KEY | DM_ACTIVE_CRYPT_KEYSIZE, &dmd) < 0 || dmd.target != DM_CRYPT)
return NULL;
if (dmd.flags & CRYPT_ACTIVATE_KEYRING_KEY)
tmp = strdup(crypt_volume_key_get_description(dmd.u.crypt.vk));
crypt_free_volume_key(dmd.u.crypt.vk);
return tmp;
}
int crypt_suspend(struct crypt_device *cd,
const char *name)
{
@@ -1771,7 +1872,7 @@ int crypt_resume_by_passphrase(struct crypt_device *cd,
&cd->u.luks1.hdr, &vk, cd);
if (r >= 0) {
keyslot = r;
r = dm_resume_and_reinstate_key(cd, name, vk->keylength, vk->key, 0);
r = dm_resume_and_reinstate_key(cd, name, vk);
if (r == -ENOTSUP)
log_err(cd, _("Resume is not supported for device %s.\n"), name);
else if (r)
@@ -1828,7 +1929,7 @@ int crypt_resume_by_keyfile_offset(struct crypt_device *cd,
goto out;
keyslot = r;
r = dm_resume_and_reinstate_key(cd, name, vk->keylength, vk->key, 0);
r = dm_resume_and_reinstate_key(cd, name, vk);
if (r)
log_err(cd, _("Error during resuming device %s.\n"), name);
out:
@@ -2110,6 +2211,29 @@ int crypt_keyslot_destroy(struct crypt_device *cd, int keyslot)
return LUKS_del_key(keyslot, &cd->u.luks1.hdr, cd);
}
int crypt_volume_key_load_in_keyring(struct crypt_device *cd, struct volume_key *vk)
{
char *seg_key_desc = NULL;
int r;
if (!vk)
return -EINVAL;
r = crypt_get_segment_key_description(cd, &seg_key_desc, CRYPT_DEFAULT_SEGMENT);
if (!r)
r = keyring_add_key_in_thread_keyring(seg_key_desc, vk->key, vk->keylength);
if (r) {
free(seg_key_desc);
log_err(cd, _("Failed to load key in kernel keyring.\n"));
} else {
crypt_volume_key_set_description(vk, seg_key_desc);
crypt_set_key_in_keyring(cd, 1);
}
return r;
}
/*
* Activation/deactivation of a device
*/
@@ -2909,6 +3033,35 @@ int crypt_get_integrity_info(struct crypt_device *cd,
return -ENOTSUP;
}
static int kernel_keyring_support(void)
{
static unsigned _checked = 0;
if (!_checked) {
_kernel_keyring_supported = keyring_check();
_checked = 1;
}
return _kernel_keyring_supported;
}
int crypt_use_keyring_for_vk(const struct crypt_device *cd)
{
uint32_t dmc_flags;
/* dm backend must be initialised */
if (!cd)
return 0;
if (!_vk_via_keyring || !kernel_keyring_support())
return 0;
if (dm_flags(DM_CRYPT, &dmc_flags))
return 1;
return (dmc_flags & DM_KERNEL_KEYRING_SUPPORTED);
}
static void __attribute__((destructor)) libcryptsetup_exit(void)
{
crypt_backend_destroy();

View File

@@ -704,6 +704,7 @@ int TCRYPT_activate(struct crypt_device *cd,
.cipher = cipher,
.offset = crypt_get_data_offset(cd),
.iv_offset = crypt_get_iv_offset(cd),
.sector_size = crypt_get_sector_size(cd),
}
};

View File

@@ -89,6 +89,40 @@ int crypt_parse_hash_integrity_mode(const char *s, char *integrity)
return 0;
}
int crypt_parse_integrity_mode(const char *s, char *integrity,
int *integrity_key_size)
{
int ks = 0, r = 0;
if (!s || !integrity)
return -EINVAL;
// FIXME: do not hardcode it here
/* AEAD modes */
if (!strcmp(s, "aead") ||
!strcmp(s, "poly1305") ||
!strcmp(s, "none")) {
strncpy(integrity, s, MAX_CIPHER_LEN);
ks = 0;
} else if (!strcmp(s, "hmac-sha256")) {
strncpy(integrity, "hmac(sha256)", MAX_CIPHER_LEN);
ks = 32;
} else if (!strcmp(s, "hmac-sha512")) {
ks = 64;
strncpy(integrity, "hmac(sha512)", MAX_CIPHER_LEN);
} else if (!strcmp(s, "cmac-aes")) {
ks = 16;
strncpy(integrity, "cmac(aes)", MAX_CIPHER_LEN);
} else
r = -EINVAL;
if (integrity_key_size)
*integrity_key_size = ks;
return r;
}
int crypt_parse_pbkdf(const char *s, const char **pbkdf)
{
const char *tmp = NULL;

View File

@@ -40,6 +40,8 @@ struct safe_allocation {
int crypt_parse_name_and_mode(const char *s, char *cipher,
int *key_nums, char *cipher_mode);
int crypt_parse_hash_integrity_mode(const char *s, char *integrity);
int crypt_parse_integrity_mode(const char *s, char *integrity,
int *integrity_key_size);
int crypt_parse_pbkdf(const char *s, const char **pbkdf);
void *crypt_safe_alloc(size_t size);

View File

@@ -371,3 +371,88 @@ char *crypt_get_base_device(const char *dev_path)
snprintf(part_path, sizeof(part_path), "/dev/%s", devname);
return strdup(part_path);
}
int lookup_by_disk_id(const char *dm_uuid)
{
struct dirent *entry;
struct stat st;
int r = 0; /* not found */
DIR *dir = opendir("/dev/disk/by-id");
if (!dir)
/* map ENOTDIR to ENOENT we'll handle both errors same */
return errno == ENOTDIR ? -ENOENT : -errno;
while ((entry = readdir(dir))) {
if (entry->d_name[0] == '.' ||
!strncmp(entry->d_name, "..", 2))
continue;
if (fstatat(dirfd(dir), entry->d_name, &st, AT_SYMLINK_NOFOLLOW)) {
r = -EINVAL;
break;
}
if (!S_ISREG(st.st_mode) && !S_ISLNK(st.st_mode))
continue;
if (!strncmp(entry->d_name, dm_uuid, strlen(dm_uuid))) {
r = 1;
break;
}
}
closedir(dir);
return r;
}
int lookup_by_sysfs_uuid_field(const char *dm_uuid, size_t max_len)
{
struct dirent *entry;
char subpath[PATH_MAX], uuid[max_len];
ssize_t s;
struct stat st;
int fd, len, r = 0; /* not found */
DIR *dir = opendir("/sys/block/");
if (!dir)
/* map ENOTDIR to ENOENT we'll handle both errors same */
return errno == ENOTDIR ? -ENOENT : -errno;
while (r != 1 && (entry = readdir(dir))) {
if (entry->d_name[0] == '.' ||
!strncmp(entry->d_name, "..", 2))
continue;
len = snprintf(subpath, PATH_MAX, "%s/%s", entry->d_name, "dm/uuid");
if (len < 0 || len >= PATH_MAX) {
r = -EINVAL;
break;
}
/* looking for dm-X/dm/uuid file, symlinks are fine */
fd = openat(dirfd(dir), subpath, O_RDONLY | O_CLOEXEC);
if (fd < 0)
continue;
if (fstat(fd, &st) || !S_ISREG(st.st_mode)) {
close(fd);
continue;
}
/* reads binary data */
s = read_buffer(fd, uuid, max_len - 1);
if (s > 0) {
uuid[s] = '\0';
if (!strncmp(uuid, dm_uuid, strlen(dm_uuid)))
r = 1;
}
close(fd);
}
closedir(dir);
return r;
}

View File

@@ -83,8 +83,8 @@ struct crypt_dm_active_device {
/* struct crypt_active_device */
uint64_t offset; /* offset in sectors */
uint64_t iv_offset; /* IV initilisation sector */
unsigned key_in_keyring:1; /* status detected key loaded via kernel keyring */
uint32_t tag_size; /* additional on-disk tag size */
uint32_t sector_size; /* encryption sector size */
} crypt;
struct {
struct device *hash_device;
@@ -136,10 +136,12 @@ int dm_create_device(struct crypt_device *cd, const char *name,
int reload);
int dm_suspend_and_wipe_key(struct crypt_device *cd, const char *name);
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);
const char *dm_get_dir(void);
int lookup_dm_dev_by_uuid(const char *uuid, const char *type);
/* These are DM helpers used only by utils_devpath file */
int dm_is_dm_device(int major, int minor);
int dm_is_dm_kernel_name(const char *name);

View File

@@ -36,6 +36,7 @@ struct volume_key *crypt_alloc_volume_key(size_t keylength, const char *key)
if (!vk)
return NULL;
vk->key_description = NULL;
vk->keylength = keylength;
/* keylength 0 is valid => no key */
@@ -49,11 +50,28 @@ struct volume_key *crypt_alloc_volume_key(size_t keylength, const char *key)
return vk;
}
void crypt_volume_key_set_description(struct volume_key *vk, const char *key_description)
{
if (vk) {
free(CONST_CAST(void*)vk->key_description);
vk->key_description = key_description;
}
}
const char *crypt_volume_key_get_description(const struct volume_key *vk)
{
if (!vk)
return NULL;
return vk->key_description;
}
void crypt_free_volume_key(struct volume_key *vk)
{
if (vk) {
crypt_memzero(vk->key, vk->keylength);
vk->keylength = 0;
free(CONST_CAST(void*)vk->key_description);
free(vk);
}
}