Commit 316ec5b3 authored by Milan Broz's avatar Milan Broz

integrity: support detached data device.

Since the kernel 4.18 there is a possibility to speficy external
data device for dm-integrity that stores all integrity tags.

The new option --data-device in integritysetup uses this feature.
parent d06defd8
......@@ -64,7 +64,7 @@ int INTEGRITY_read_sb(struct crypt_device *cd, struct crypt_params_integrity *pa
struct superblock sb;
int r;
r = INTEGRITY_read_superblock(cd, crypt_data_device(cd), 0, &sb);
r = INTEGRITY_read_superblock(cd, crypt_metadata_device(cd), 0, &sb);
if (r)
return r;
......@@ -204,7 +204,10 @@ int INTEGRITY_activate(struct crypt_device *cd,
};
int r;
r = INTEGRITY_data_sectors(cd, dmdi.data_device,
if (dmdi.data_device != crypt_metadata_device(cd))
dmdi.u.integrity.meta_device = crypt_metadata_device(cd);
r = INTEGRITY_data_sectors(cd, crypt_metadata_device(cd),
dmdi.u.integrity.offset * SECTOR_SIZE, &dmdi.size);
if (r < 0)
return r;
......@@ -228,6 +231,12 @@ int INTEGRITY_activate(struct crypt_device *cd,
if (r)
return r;
if (dmdi.u.integrity.meta_device) {
r = device_block_adjust(cd, dmdi.u.integrity.meta_device, DEV_EXCL, 0, NULL, NULL);
if (r)
return r;
}
r = dm_create_device(cd, name, "INTEGRITY", &dmdi, 0);
if (r < 0 && (dm_flags(cd, DM_INTEGRITY, &dmi_flags) || !(dmi_flags & DM_INTEGRITY_SUPPORTED))) {
log_err(cd, _("Kernel doesn't support dm-integrity mapping."));
......@@ -260,6 +269,9 @@ int INTEGRITY_format(struct crypt_device *cd,
int r;
uuid_t tmp_uuid_bin;
if (dmdi.data_device != crypt_metadata_device(cd))
dmdi.u.integrity.meta_device = crypt_metadata_device(cd);
if (params) {
dmdi.u.integrity.journal_size = params->journal_size;
dmdi.u.integrity.journal_watermark = params->journal_watermark;
......@@ -287,6 +299,12 @@ int INTEGRITY_format(struct crypt_device *cd,
if (r)
return r;
if (dmdi.u.integrity.meta_device) {
r = device_block_adjust(cd, dmdi.u.integrity.meta_device, DEV_EXCL, 0, NULL, NULL);
if (r)
return r;
}
/* There is no data area, we can actually use fake zeroed key */
if (params && params->integrity_key_size)
dmdi.u.integrity.vk = crypt_alloc_volume_key(params->integrity_key_size, NULL);
......
......@@ -696,6 +696,7 @@ static char *get_dm_integrity_params(struct crypt_dm_active_device *dmd, uint32_
return NULL;
max_size = strlen(device_block_path(dmd->data_device)) +
(dmd->u.integrity.meta_device ? strlen(device_block_path(dmd->u.integrity.meta_device)) : 0) +
(dmd->u.integrity.vk ? dmd->u.integrity.vk->keylength * 2 : 0) +
(dmd->u.integrity.journal_integrity_key ? dmd->u.integrity.journal_integrity_key->keylength * 2 : 0) +
(dmd->u.integrity.journal_crypt_key ? dmd->u.integrity.journal_crypt_key->keylength * 2 : 0) +
......@@ -810,6 +811,13 @@ static char *get_dm_integrity_params(struct crypt_dm_active_device *dmd, uint32_
strncat(features, feature, sizeof(features) - strlen(features) - 1);
}
if (dmd->u.integrity.meta_device) {
num_options++;
snprintf(feature, sizeof(feature), "meta_device:%s ",
device_block_path(dmd->u.integrity.meta_device));
strncat(features, feature, sizeof(features) - strlen(features) - 1);
}
if (flags & CRYPT_ACTIVATE_RECOVERY)
mode = 'R';
else if (flags & CRYPT_ACTIVATE_NO_JOURNAL)
......@@ -1814,7 +1822,7 @@ static int _dm_query_integrity(struct crypt_device *cd,
unsigned int i, features, val;
ssize_t len;
int r;
struct device *data_device = NULL;
struct device *data_device = NULL, *meta_device = NULL;
char *integrity = NULL, *journal_crypt = NULL, *journal_integrity = NULL;
struct volume_key *vk = NULL;
......@@ -1917,6 +1925,14 @@ static int _dm_query_integrity(struct crypt_device *cd,
if (r < 0)
goto err;
}
} else if (!strncmp(arg, "meta_device:", 12) && !meta_device) {
if (get_flags & DM_ACTIVE_DEVICE) {
str = crypt_lookup_dev(&arg[12]);
r = device_alloc(cd, &meta_device, str);
free(str);
if (r < 0 && r != -ENOTBLK)
goto err;
}
} else if (!strncmp(arg, "journal_crypt:", 14) && !journal_crypt) {
str = &arg[14];
arg = strsep(&str, ":");
......@@ -1952,6 +1968,8 @@ static int _dm_query_integrity(struct crypt_device *cd,
if (data_device)
dmd->data_device = data_device;
if (meta_device)
dmd->u.integrity.meta_device = meta_device;
if (integrity)
dmd->u.integrity.integrity = integrity;
if (journal_crypt)
......@@ -1963,6 +1981,7 @@ static int _dm_query_integrity(struct crypt_device *cd,
return 0;
err:
device_free(cd, data_device);
device_free(cd, meta_device);
free(integrity);
free(journal_crypt);
free(journal_integrity);
......
......@@ -631,7 +631,8 @@ int crypt_set_data_device(struct crypt_device *cd, const char *device)
log_dbg(cd, "Setting ciphertext data device to %s.", device ?: "(none)");
if (!isLUKS1(cd->type) && !isLUKS2(cd->type) && !isVERITY(cd->type)) {
if (!isLUKS1(cd->type) && !isLUKS2(cd->type) && !isVERITY(cd->type) &&
!isINTEGRITY(cd->type)) {
log_err(cd, _("This operation is not supported for this device type."));
return -EINVAL;
}
......@@ -1220,6 +1221,9 @@ static int _init_by_name_integrity(struct crypt_device *cd, const char *name)
cd->u.integrity.params.journal_integrity_key_size = dmd.u.integrity.journal_integrity_key->keylength;
if (dmd.u.integrity.journal_crypt_key)
cd->u.integrity.params.integrity_key_size = dmd.u.integrity.journal_crypt_key->keylength;
cd->metadata_device = dmd.u.integrity.meta_device;
integrity_type = 1;
}
out:
......
......@@ -122,6 +122,8 @@ struct crypt_dm_active_device {
const char *journal_crypt;
struct volume_key *journal_crypt_key;
struct device *meta_device;
} integrity;
} u;
};
......
......@@ -19,9 +19,9 @@ Integritysetup supports these operations:
.IP
Formats <device> (calculates space and dm-integrity superblock and wipes the device).
\fB<options>\fR can be [\-\-batch\-mode, \-\-no\-wipe, \-\-journal\-size, \-\-interleave\-sectors,
\-\-tag\-size, \-\-integrity, \-\-integrity\-key\-size, \-\-integrity\-key\-file, \-\-sector\-size,
\-\-progress-frequency]
\fB<options>\fR can be [\-\-data\-device, \-\-batch\-mode, \-\-no\-wipe, \-\-journal\-size,
\-\-interleave\-sectors, \-\-tag\-size, \-\-integrity, \-\-integrity\-key\-size,
\-\-integrity\-key\-file, \-\-sector\-size, \-\-progress-frequency]
.PP
\fIopen\fR <device> <name>
......@@ -30,9 +30,10 @@ Formats <device> (calculates space and dm-integrity superblock and wipes the dev
.IP
Open a mapping with <name> backed by device <device>.
\fB<options>\fR can be [\-\-batch\-mode, \-\-journal\-watermark, \-\-journal\-commit\-time,
\-\-buffer\-sectors, \-\-integrity, \-\-integrity\-key\-size, \-\-integrity\-key\-file,
\-\-integrity\-no\-journal, \-\-integrity\-recalculate, \-\-integrity\-recovery\-mode]
\fB<options>\fR can be [\-\-data\-device, \-\-batch\-mode, \-\-journal\-watermark,
\-\-journal\-commit\-time, \-\-buffer\-sectors, \-\-integrity, \-\-integrity\-key\-size,
\-\-integrity\-key\-file, \-\-integrity\-no\-journal, \-\-integrity\-recalculate,
\-\-integrity\-recovery\-mode]
.PP
\fIclose\fR <name>
......@@ -97,6 +98,10 @@ Size of the integrity tag per-sector (here the integrity function will store aut
\fBNOTE:\fR The size can be smaller that output size of the hash function, in that case only
part of the hash will be stored.
.TP
.B "\-\-data\-device"
Specify a separate data device that contains existing data. The <device> then will contain
calculated integrity tags and journal for this data device.
.TP
.B "\-\-sector\-size, \-s BYTES"
Sector size (power of two: 512, 1024, 2048, 4096).
.TP
......
......@@ -39,6 +39,8 @@ static int opt_buffer_sectors = 0;
static int opt_no_wipe = 0;
static const char *opt_data_device = NULL;
static const char *opt_integrity = DEFAULT_ALG_NAME;
static const char *opt_integrity_key_file = NULL;
static int opt_integrity_key_size = 0;
......@@ -215,7 +217,7 @@ static int action_format(int arg)
if (r)
goto out;
r = crypt_init(&cd, action_argv[0]);
r = crypt_init_data_device(&cd, action_argv[0], opt_data_device);
if (r < 0)
goto out;
......@@ -308,12 +310,13 @@ static int action_open(int arg)
if (r)
goto out;
if ((r = crypt_init(&cd, action_argv[0])))
if ((r = crypt_init_data_device(&cd, action_argv[0], opt_data_device)))
goto out;
r = crypt_load(cd, CRYPT_INTEGRITY, &params);
if (r)
goto out;
r = crypt_activate_by_volume_key(cd, action_argv[1], integrity_key,
opt_integrity_key_size, activate_flags);
out:
......@@ -344,7 +347,7 @@ static int action_status(int arg)
struct crypt_params_integrity ip = {};
struct crypt_device *cd = NULL;
char *backing_file;
const char *device;
const char *device, *metadata_device;
int path = 0, r = 0;
/* perhaps a path, not a dm device name */
......@@ -389,12 +392,21 @@ static int action_status(int arg)
log_std(" tag size: %u\n", ip.tag_size);
log_std(" integrity: %s\n", ip.integrity ?: "(none)");
device = crypt_get_device_name(cd);
log_std(" device: %s\n", device);
metadata_device = crypt_get_metadata_device_name(cd);
log_std(" device: %s%s\n", device, metadata_device ? " (detached)" : "");
if (crypt_loop_device(device)) {
backing_file = crypt_loop_backing_file(device);
log_std(" loop: %s\n", backing_file);
free(backing_file);
}
if (metadata_device) {
log_std(" metadata device: %s\n", metadata_device);
if (crypt_loop_device(metadata_device)) {
backing_file = crypt_loop_backing_file(metadata_device);
log_std(" loop: %s\n", backing_file);
free(backing_file);
}
}
log_std(" sector size: %u bytes\n", crypt_get_sector_size(cd));
log_std(" interleave sectors: %u\n", ip.interleave_sectors);
log_std(" size: %" PRIu64 " sectors\n", cad.size);
......@@ -513,6 +525,8 @@ int main(int argc, const char **argv)
{ "progress-frequency", '\0', POPT_ARG_INT, &opt_progress_frequency, 0, N_("Progress line update (in seconds)"), N_("secs") },
{ "no-wipe", '\0', POPT_ARG_NONE, &opt_no_wipe, 0, N_("Do not wipe device after format"), NULL },
{ "data-device", '\0', POPT_ARG_STRING, &opt_data_device, 0, N_("Path to data device (if separated)"), N_("path") },
{ "journal-size", 'j', POPT_ARG_STRING,&opt_journal_size_str, 0, N_("Journal size"), N_("bytes") },
{ "interleave-sectors", '\0', POPT_ARG_INT, &opt_interleave_sectors, 0, N_("Interleave sectors"), N_("SECTORS") },
{ "journal-watermark", '\0', POPT_ARG_INT, &opt_journal_watermark, 0, N_("Journal watermark"),N_("percent") },
......
......@@ -8,6 +8,7 @@ INTSETUP_LIB_VALGRIND=../.libs
DEV_NAME=dmc_test
DEV=test123.img
DEV2=test124.img
KEY_FILE=key.img
dmremove() { # device
......@@ -17,13 +18,13 @@ dmremove() { # device
cleanup() {
[ -b /dev/mapper/$DEV_NAME ] && dmremove $DEV_NAME
rm -f $DEV $KEY_FILE >/dev/null 2>&1
rm -f $DEV $DEV2 $KEY_FILE >/dev/null 2>&1
}
fail()
{
echo
[ -n "$1" ] && echo "FAILED at line $(caller)"
echo "FAILED at line $(caller)"
cleanup
exit 100
}
......@@ -34,10 +35,27 @@ skip()
exit 77
}
function dm_integrity_features()
{
VER_STR=$(dmsetup targets | grep integrity | cut -f2 -dv)
[ -z "$VER_STR" ] && skip "Cannot find dm-integrity target, test skipped."
VER_MAJ=$(echo $VER_STR | cut -f 1 -d.)
VER_MIN=$(echo $VER_STR | cut -f 2 -d.)
VER_PTC=$(echo $VER_STR | cut -f 3 -d.)
[ $VER_MAJ -lt 1 ] && return
[ $VER_MIN -gt 1 ] && {
DM_INTEGRITY_META=1
DM_INTEGRITY_RECALC=1
}
}
add_device() {
cleanup
dd if=/dev/urandom of=$KEY_FILE bs=1 count=512 >/dev/null 2>&1
dd if=/dev/zero of=$DEV bs=1M count=32 >/dev/null 2>&1
dd if=/dev/zero of=$DEV2 bs=1M count=32 >/dev/null 2>&1
sync
}
......@@ -83,6 +101,18 @@ function valgrind_run()
INFOSTRING="$(basename ${BASH_SOURCE[1]})-line-${BASH_LINENO[0]}" ./valg.sh ${INTSETUP_VALGRIND} "$@"
}
int_check_sum_only() # checksum
{
VSUM=$(sha256sum /dev/mapper/$DEV_NAME | cut -d' ' -f 1)
if [ "$VSUM" = "$1" ] ; then
echo -n "[CHECKSUM OK]"
else
echo "[FAIL]"
echo " Expecting $1 got $VSUM."
fail
fi
}
int_check_sum() # alg checksum [keyfile keysize]
{
if [ -n "$4" ] ; then
......@@ -97,14 +127,7 @@ int_check_sum() # alg checksum [keyfile keysize]
$INTSETUP open $DEV $DEV_NAME --integrity $1 $KEY_PARAMS || fail "Cannot activate device."
VSUM=$(sha256sum /dev/mapper/$DEV_NAME | cut -d' ' -f 1)
if [ "$VSUM" = "$2" ] ; then
echo -n "[CHECKSUM OK]"
else
echo "[FAIL]"
echo " Expecting $2 got $VSUM."
fail
fi
int_check_sum_only $2
}
intformat() # alg alg_out tagsize sector_size csum [keyfile keysize]
......@@ -263,7 +286,7 @@ int_mode() # alg tag_size sector_size [keyfile keysize]
[ -n "$VALG" ] && valgrind_setup && INTSETUP=valgrind_run
which hexdump >/dev/null 2>&1 || skip "WARNING: hexdump tool required."
modprobe dm-integrity >/dev/null 2>&1
dmsetup targets | grep integrity >/dev/null 2>&1 || skip "Cannot find dm-integrity target, test skipped."
dm_integrity_features
add_device
intformat crc32c crc32c 4 512 08f63eb27fb9ce2ce903b0a56429c68ce5e209253ba42154841ef045a53839d7
......@@ -312,11 +335,26 @@ int_mode hmac-sha256 32 512 $KEY_FILE 32
int_mode hmac-sha256 32 4096 $KEY_FILE 32
echo -n "Recalculate tags in-kernel:"
$INTSETUP format -q $DEV --no-wipe || fail "Cannot format device."
$INTSETUP open $DEV $DEV_NAME --integrity-recalculate || fail "Cannot activate device with journal."
$INTSETUP dump $DEV | grep -q recalculating
if [ $? -eq 0 ] ; then
dd if=/dev/mapper/$DEV_NAME of=/dev/null bs=1M 2>/dev/null || fail "Cannot tags recalculate in-kernel"
add_device
if [ -n "$DM_INTEGRITY_RECALC" ] ; then
$INTSETUP format -q $DEV --no-wipe || fail "Cannot format device."
$INTSETUP open $DEV $DEV_NAME --integrity-recalculate || fail "Cannot activate device."
dd if=/dev/mapper/$DEV_NAME of=/dev/null bs=1M 2>/dev/null || fail "Cannot recalculate tags in-kernel"
int_check_sum_only 08f63eb27fb9ce2ce903b0a56429c68ce5e209253ba42154841ef045a53839d7
$INTSETUP close $DEV_NAME fail "Cannot deactivate device."
echo "[OK]"
else
echo "[N/A]"
fi
echo -n "Recalculate tags in-kernel + separate device:"
if [ -n "$DM_INTEGRITY_META" ] ; then
add_device
$INTSETUP format -q $DEV --data-device $DEV2 || fail "Cannot format device."
$INTSETUP open $DEV --data-device $DEV2 $DEV_NAME || fail "Cannot activate device."
int_check_sum_only 83ee47245398adee79bd9c0a8bc57b821e92aba10f5f9ade8a5d1fae4d8c4302
$INTSETUP status $DEV_NAME | grep -q 'metadata device:' || fail
$INTSETUP close $DEV_NAME fail "Cannot deactivate device."
echo "[OK]"
else
echo "[N/A]"
......
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