Commit fef5121c authored by Milan Broz's avatar Milan Broz

veritysetup: add support for --check-at-most-once option.

The kernel 4.17 will include a new dm-verity flag that
instructs kernel to verify data blocks only once.

This patch adds support for it to libcryptsetup and veritysetup.

This flag can be dangerous; if you can control underlying device
(you can change its content after it was verified) it will no longer
prevent reading tampered data and also it does not prevent to silent
data corrruptions that appears after the block was once read.
parent c84983f9
......@@ -932,6 +932,8 @@ int crypt_keyslot_destroy(struct crypt_device *cd, int keyslot);
#define CRYPT_ACTIVATE_RECOVERY (1 << 13)
/** ignore persistently stored flags */
#define CRYPT_ACTIVATE_IGNORE_PERSISTENT (1 << 14)
/** dm-verity: check_at_most_once - check data blocks only the first time */
#define CRYPT_ACTIVATE_CHECK_AT_MOST_ONCE (1 << 15)
/**
* Active device runtime attributes
......
......@@ -185,6 +185,7 @@ static void _dm_set_verity_compat(unsigned verity_maj,
* ignore_zero_blocks since 1.3 (kernel 4.5)
* (but some dm-verity targets 1.2 don't support it)
* FEC is added in 1.3 as well.
* Check at most once is added in 1.4 (kernel 4.17).
*/
if (_dm_satisfies_version(1, 3, 0, verity_maj, verity_min, verity_patch)) {
_dm_flags |= DM_VERITY_ON_CORRUPTION_SUPPORTED;
......@@ -622,6 +623,8 @@ static char *get_dm_verity_params(struct crypt_params_verity *vp,
num_options++;
if (flags & CRYPT_ACTIVATE_IGNORE_ZERO_BLOCKS)
num_options++;
if (flags & CRYPT_ACTIVATE_CHECK_AT_MOST_ONCE)
num_options++;
if (dmd->u.verity.fec_device) {
num_options += 8;
......@@ -633,10 +636,11 @@ static char *get_dm_verity_params(struct crypt_params_verity *vp,
*fec_features = '\0';
if (num_options)
snprintf(features, sizeof(features)-1, " %d%s%s%s", num_options,
snprintf(features, sizeof(features)-1, " %d%s%s%s%s", num_options,
(flags & CRYPT_ACTIVATE_IGNORE_CORRUPTION) ? " ignore_corruption" : "",
(flags & CRYPT_ACTIVATE_RESTART_ON_CORRUPTION) ? " restart_on_corruption" : "",
(flags & CRYPT_ACTIVATE_IGNORE_ZERO_BLOCKS) ? " ignore_zero_blocks" : "");
(flags & CRYPT_ACTIVATE_IGNORE_ZERO_BLOCKS) ? " ignore_zero_blocks" : "",
(flags & CRYPT_ACTIVATE_CHECK_AT_MOST_ONCE) ? " check_at_most_once" : "");
else
*features = '\0';
......@@ -1221,7 +1225,8 @@ int dm_create_device(struct crypt_device *cd, const char *name,
if (r == -EINVAL && dmd_flags & (CRYPT_ACTIVATE_IGNORE_CORRUPTION|
CRYPT_ACTIVATE_RESTART_ON_CORRUPTION|
CRYPT_ACTIVATE_IGNORE_ZERO_BLOCKS) &&
CRYPT_ACTIVATE_IGNORE_ZERO_BLOCKS|
CRYPT_ACTIVATE_CHECK_AT_MOST_ONCE) &&
!(dmt_flags & DM_VERITY_ON_CORRUPTION_SUPPORTED))
log_err(cd, _("Requested dm-verity data corruption handling options are not supported.\n"));
......@@ -1684,6 +1689,8 @@ static int _dm_query_verity(uint32_t get_flags,
dmd->flags |= CRYPT_ACTIVATE_RESTART_ON_CORRUPTION;
else if (!strcasecmp(arg, "ignore_zero_blocks"))
dmd->flags |= CRYPT_ACTIVATE_IGNORE_ZERO_BLOCKS;
else if (!strcasecmp(arg, "check_at_most_once"))
dmd->flags |= CRYPT_ACTIVATE_CHECK_AT_MOST_ONCE;
else if (!strcasecmp(arg, "use_fec_from_device")) {
str = strsep(&params, " ");
str2 = crypt_lookup_dev(str);
......
......@@ -39,7 +39,8 @@ Creates a mapping with <name> backed by device <data_device> and using
The <root_hash> is a hexadecimal string.
\fB<options>\fR can be [\-\-hash-offset, \-\-no-superblock,
\-\-ignore-corruption or \-\-restart-on-corruption, \-\-ignore-zero-blocks]
\-\-ignore-corruption or \-\-restart-on-corruption, \-\-ignore-zero-blocks,
\-\-check-at-most-once]
If option \-\-no-superblock is used, you have to use as the same options
as in initial format operation.
......@@ -133,6 +134,15 @@ and always directly return zeroes instead.
\fBWARNING:\fR Use this option only in very specific cases.
This option is available since Linux kernel version 4.5.
.TP
.B "\-\-check-at-most-once"
Instruct kernel to verify blocks only the first time they are read
from the data device, rather than every time.
\fBWARNING:\fR It provides a reduced level of security because only
offline tampering of the data device's content will be detected,
not online tampering.
This option is available since Linux kernel version 4.17.
.TP
.B "\-\-hash=hash"
Hash algorithm for dm-verity. For default see \-\-help option.
.TP
......
......@@ -39,6 +39,7 @@ static const char *opt_uuid = NULL;
static int opt_restart_on_corruption = 0;
static int opt_ignore_corruption = 0;
static int opt_ignore_zero_blocks = 0;
static int opt_check_at_most_once = 0;
static int opt_version_mode = 0;
......@@ -153,6 +154,8 @@ static int _activate(const char *dm_device,
activate_flags |= CRYPT_ACTIVATE_RESTART_ON_CORRUPTION;
if (opt_ignore_zero_blocks)
activate_flags |= CRYPT_ACTIVATE_IGNORE_ZERO_BLOCKS;
if (opt_check_at_most_once)
activate_flags |= CRYPT_ACTIVATE_CHECK_AT_MOST_ONCE;
if (use_superblock) {
params.flags = flags;
......@@ -317,11 +320,13 @@ static int action_status(int arg)
}
if (cad.flags & (CRYPT_ACTIVATE_IGNORE_CORRUPTION|
CRYPT_ACTIVATE_RESTART_ON_CORRUPTION|
CRYPT_ACTIVATE_IGNORE_ZERO_BLOCKS))
log_std(" flags: %s%s%s\n",
CRYPT_ACTIVATE_IGNORE_ZERO_BLOCKS|
CRYPT_ACTIVATE_CHECK_AT_MOST_ONCE))
log_std(" flags: %s%s%s%s\n",
(cad.flags & CRYPT_ACTIVATE_IGNORE_CORRUPTION) ? "ignore_corruption " : "",
(cad.flags & CRYPT_ACTIVATE_RESTART_ON_CORRUPTION) ? "restart_on_corruption " : "",
(cad.flags & CRYPT_ACTIVATE_IGNORE_ZERO_BLOCKS) ? "ignore_zero_blocks" : "");
(cad.flags & CRYPT_ACTIVATE_IGNORE_ZERO_BLOCKS) ? "ignore_zero_blocks " : "",
(cad.flags & CRYPT_ACTIVATE_CHECK_AT_MOST_ONCE) ? "check_at_most_once" : "");
}
out:
crypt_free(cd);
......@@ -439,6 +444,7 @@ int main(int argc, const char **argv)
{ "restart-on-corruption", 0,POPT_ARG_NONE,&opt_restart_on_corruption, 0, N_("Restart kernel if corruption is detected"), NULL },
{ "ignore-corruption", 0, POPT_ARG_NONE, &opt_ignore_corruption, 0, N_("Ignore corruption, log it only"), NULL },
{ "ignore-zero-blocks", 0, POPT_ARG_NONE, &opt_ignore_zero_blocks, 0, N_("Do not verify zeroed blocks"), NULL },
{ "check-at-most-once", 0, POPT_ARG_NONE, &opt_check_at_most_once, 0, N_("Verify data block only the first time it is read."), NULL },
POPT_TABLEEND
};
......
......@@ -367,7 +367,7 @@ echo "badpw" | $CRYPTSETUP luksKillSlot $LOOPDEV 2 --key-file=- 2>/dev/null && f
echo "badpw" | $CRYPTSETUP luksKillSlot $LOOPDEV 2 --key-file=- -q 2>/dev/null && fail
$CRYPTSETUP luksDump $LOOPDEV | grep -q "2: luks2" || fail
# kill slot using passphrase from 1
echo $PWD2 | $CRYPTSETUP luksKillSlot $LOOPDEV 2 2>/dev/null || fail #XXX
echo $PWD2 | $CRYPTSETUP luksKillSlot $LOOPDEV 2 2>/dev/null || fail
$CRYPTSETUP luksDump $LOOPDEV | grep -q "2: luks2" && fail
# remove key0 / slot 0
echo $PWD1 | $CRYPTSETUP luksRemoveKey $LOOPDEV || fail
......
......@@ -59,7 +59,7 @@ function check_exists()
[ -b /dev/mapper/$DEV_NAME ] || fail
}
function check_version()
function check_version() # MAJ MIN
{
VER_STR=$(dmsetup targets | grep verity | cut -f 3 -dv)
[ -z "$VER_STR" ] && fail "Failed to parse dm-verity version."
......@@ -67,9 +67,8 @@ function check_version()
VER_MAJ=$(echo $VER_STR | cut -f 1 -d.)
VER_MIN=$(echo $VER_STR | cut -f 2 -d.)
# option supported in 1.3
test $VER_MAJ -gt 1 && return 0
test $VER_MIN -ge 3 && return 0
test $VER_MAJ -gt $1 && return 0
test $VER_MIN -ge $2 && return 0
return 1
}
......@@ -383,7 +382,7 @@ check_root_hash 4096 ef29c902d87350f1da4bfa536e16cebc162a909bf89abe448b81ec500d4
check_root_hash 1024 d0e9163ca8844aaa2e88fe5265a8c5d9ee494a99 $SALT 1 sha1 8388608
check_root_hash 1024 73509e8e868be6b8ac939817a98a3d35121413b2 dadada 1 sha1 8388608
if check_version ; then
if check_version 1 3; then
echo "Verity data corruption options test."
SALT=e48da609055204e89ae53b655ca2216dd983cf3cb829f34f63a297d106d53e2d
HASH=9de18652fe74edfb9b805aaed72ae2aa48f94333f1ba5c452ac33b1c39325174
......@@ -392,6 +391,9 @@ if check_version ; then
check_option 512 $HASH $SALT 1 sha256 "--restart-on-corruption" "restart_on_corruption"
check_option 512 $HASH $SALT 1 sha256 "--ignore-zero-blocks" "ignore_zero_blocks"
check_option 512 $HASH $SALT 1 sha256 "--ignore-corruption --ignore-zero-blocks" "ignore_corruption"
if check_version 1 4; then
check_option 512 $HASH $SALT 1 sha256 "--check-at-most-once" "check_at_most_once"
fi
fi
echo "Veritysetup [hash-offset bigger than 2G works] "
......@@ -405,7 +407,7 @@ checkOverlapBug 2097152 1228800 350 4096 # data-hash overlap
checkOverlapBug 2097152 0 350 4096 1228800 # data-fec overlap
checkOverlapBug 10240000 256000 400 512 256512 # hash-fec overlap
if check_version ; then
if check_version 1 3; then
echo "Veritysetup [FEC tests]"
for INDEX in {1..4}; do
# in the first iteration check if we can use FEC (it can be compiled-out)
......
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