Commit c56bdee1 authored by Milan Broz's avatar Milan Broz

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.
parent 894e7b93
......@@ -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 */
......@@ -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
......
......@@ -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;
......
This diff is collapsed.
......@@ -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),
}
};
......
......@@ -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;
......
......@@ -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),
}
};
......
......@@ -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();
......
......@@ -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),
}
};
......
......@@ -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;
......
......@@ -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);
......
......@@ -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;
}
......@@ -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);
......
......@@ -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);
}
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment