diff --git a/lib/libcryptsetup.h b/lib/libcryptsetup.h index 15138d6c..80bbf5c8 100644 --- a/lib/libcryptsetup.h +++ b/lib/libcryptsetup.h @@ -3,8 +3,8 @@ * * Copyright (C) 2004, Jana Saout * Copyright (C) 2004-2007, Clemens Fruhwirth - * Copyright (C) 2009-2015, Red Hat, Inc. All rights reserved. - * Copyright (C) 2009-2015, Milan Broz + * Copyright (C) 2009-2016, Red Hat, Inc. All rights reserved. + * Copyright (C) 2009-2016, Milan Broz * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -667,6 +667,12 @@ int crypt_keyslot_destroy(struct crypt_device *cd, int keyslot); #define CRYPT_ACTIVATE_SAME_CPU_CRYPT (1 << 6) /** use submit_from_crypt_cpus for dm-crypt */ #define CRYPT_ACTIVATE_SUBMIT_FROM_CRYPT_CPUS (1 << 7) +/** dm-verity: ignore_corruption flag - ignore corruption, log it only */ +#define CRYPT_ACTIVATE_IGNORE_CORRUPTION (1 << 8) +/** dm-verity: restart_on_corruption flag - restart kernel on corruption */ +#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) /** diff --git a/lib/libdevmapper.c b/lib/libdevmapper.c index ca2084fc..b6cd3a7c 100644 --- a/lib/libdevmapper.c +++ b/lib/libdevmapper.c @@ -3,8 +3,8 @@ * * Copyright (C) 2004, Jana Saout * Copyright (C) 2004-2007, Clemens Fruhwirth - * Copyright (C) 2009-2015, Red Hat, Inc. All rights reserved. - * Copyright (C) 2009-2015, Milan Broz + * Copyright (C) 2009-2016, Red Hat, Inc. All rights reserved. + * Copyright (C) 2009-2016, Milan Broz * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -159,6 +159,15 @@ static void _dm_set_verity_compat(const char *dm_version, unsigned verity_maj, { if (verity_maj > 0) _dm_crypt_flags |= DM_VERITY_SUPPORTED; + else + return; + /* + * ignore_corruption, restart_on corruption is available since 1.2 (kernel 4.1) + * ignore_zero_blocks since 1.3 (kernel 4.5) + * (but some dm-verity targets 1.2 don't support it) + */ + if (_dm_satisfies_version(1, 3, verity_maj, verity_min)) + _dm_crypt_flags |= DM_VERITY_ON_CORRUPTION_SUPPORTED; log_dbg("Detected dm-verity version %i.%i.%i.", verity_maj, verity_min, verity_patch); @@ -357,14 +366,35 @@ out: /* https://gitlab.com/cryptsetup/cryptsetup/wikis/DMVerity */ static char *get_dm_verity_params(struct crypt_params_verity *vp, - struct crypt_dm_active_device *dmd) + struct crypt_dm_active_device *dmd, uint32_t flags) { - int max_size, r; + int max_size, r, num_options = 0; char *params = NULL, *hexroot = NULL, *hexsalt = NULL; + char features[256]; if (!vp || !dmd) return NULL; + /* These flags are not compatible */ + if ((flags & CRYPT_ACTIVATE_IGNORE_CORRUPTION) && + (flags & CRYPT_ACTIVATE_RESTART_ON_CORRUPTION)) + flags &= ~CRYPT_ACTIVATE_IGNORE_CORRUPTION; + + if (flags & CRYPT_ACTIVATE_IGNORE_CORRUPTION) + num_options++; + if (flags & CRYPT_ACTIVATE_RESTART_ON_CORRUPTION) + num_options++; + if (flags & CRYPT_ACTIVATE_IGNORE_ZERO_BLOCKS) + num_options++; + + if (num_options) + snprintf(features, sizeof(features)-1, " %d%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" : ""); + else + *features = '\0'; + hexroot = crypt_safe_alloc(dmd->u.verity.root_hash_size * 2 + 1); if (!hexroot) goto out; @@ -388,12 +418,12 @@ static char *get_dm_verity_params(struct crypt_params_verity *vp, goto out; r = snprintf(params, max_size, - "%u %s %s %u %u %" PRIu64 " %" PRIu64 " %s %s %s", + "%u %s %s %u %u %" PRIu64 " %" PRIu64 " %s %s %s %s", vp->hash_type, device_block_path(dmd->data_device), device_block_path(dmd->u.verity.hash_device), vp->data_block_size, vp->hash_block_size, vp->data_size, dmd->u.verity.hash_offset, - vp->hash_name, hexroot, hexsalt); + vp->hash_name, hexroot, hexsalt, features); if (r < 0 || r >= max_size) { crypt_safe_free(params); params = NULL; @@ -676,7 +706,7 @@ int dm_create_device(struct crypt_device *cd, const char *name, if (dmd->target == DM_CRYPT) table_params = get_dm_crypt_params(dmd, dmd_flags); else if (dmd->target == DM_VERITY) - table_params = get_dm_verity_params(dmd->u.verity.vp, dmd); + table_params = get_dm_verity_params(dmd->u.verity.vp, dmd, dmd_flags); r = _dm_create_device(name, type, dmd->data_device, dmd_flags, dmd->uuid, dmd->size, table_params, reload); @@ -696,7 +726,13 @@ int dm_create_device(struct crypt_device *cd, const char *name, if (r == -EINVAL && dmd_flags & (CRYPT_ACTIVATE_SAME_CPU_CRYPT|CRYPT_ACTIVATE_SUBMIT_FROM_CRYPT_CPUS) && !(dm_flags() & (DM_SAME_CPU_CRYPT_SUPPORTED|DM_SUBMIT_FROM_CRYPT_CPUS_SUPPORTED))) - log_err(cd, _("Requested dmcrypt performance options are not supported.\n")); + log_err(cd, _("Requested dm-crypt performance options are not supported.\n")); + + if (r == -EINVAL && dmd_flags & (CRYPT_ACTIVATE_IGNORE_CORRUPTION| + CRYPT_ACTIVATE_RESTART_ON_CORRUPTION| + CRYPT_ACTIVATE_IGNORE_ZERO_BLOCKS) && + !(dm_flags() & DM_VERITY_ON_CORRUPTION_SUPPORTED)) + log_err(cd, _("Requested dm-verity data corruption handling options are not supported.\n")); crypt_safe_free(table_params); dm_exit_context(); @@ -893,7 +929,7 @@ static int _dm_query_crypt(uint32_t get_flags, return -EINVAL; } - /* All parameters shold be processed */ + /* All parameters should be processed */ if (params) return -EINVAL; } @@ -936,7 +972,8 @@ static int _dm_query_verity(uint32_t get_flags, uint32_t val32; uint64_t val64; ssize_t len; - char *str, *str2; + char *str, *str2, *arg; + unsigned int i; int r; if (get_flags & DM_ACTIVE_VERITY_PARAMS) @@ -1032,8 +1069,6 @@ static int _dm_query_verity(uint32_t get_flags, /* salt */ str = strsep(¶ms, " "); - if (params) - return -EINVAL; if (vp) { if (!strcmp(str, "-")) { vp->salt_size = 0; @@ -1047,6 +1082,33 @@ static int _dm_query_verity(uint32_t get_flags, } } + /* Features section, available since verity target version 1.3 */ + if (params) { + /* Number of arguments */ + val64 = strtoull(params, ¶ms, 10); + if (*params != ' ') + return -EINVAL; + params++; + + for (i = 0; i < val64; i++) { + if (!params) + return -EINVAL; + arg = strsep(¶ms, " "); + if (!strcasecmp(arg, "ignore_corruption")) + dmd->flags |= CRYPT_ACTIVATE_IGNORE_CORRUPTION; + else if (!strcasecmp(arg, "restart_on_corruption")) + dmd->flags |= CRYPT_ACTIVATE_RESTART_ON_CORRUPTION; + else if (!strcasecmp(arg, "ignore_zero_blocks")) + dmd->flags |= CRYPT_ACTIVATE_IGNORE_ZERO_BLOCKS; + else /* unknown option */ + return -EINVAL; + } + + /* All parameters should be processed */ + if (params) + return -EINVAL; + } + return 0; } diff --git a/lib/setup.c b/lib/setup.c index 0bf5bcfe..307e15cc 100644 --- a/lib/setup.c +++ b/lib/setup.c @@ -3,8 +3,8 @@ * * Copyright (C) 2004, Jana Saout * Copyright (C) 2004-2007, Clemens Fruhwirth - * Copyright (C) 2009-2012, Red Hat, Inc. All rights reserved. - * Copyright (C) 2009-2014, Milan Broz + * Copyright (C) 2009-2016, Red Hat, Inc. All rights reserved. + * Copyright (C) 2009-2016, Milan Broz * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -2009,7 +2009,7 @@ int crypt_activate_by_volume_key(struct crypt_device *cd, } r = VERITY_activate(cd, name, volume_key, volume_key_size, - &cd->u.verity.hdr, CRYPT_ACTIVATE_READONLY); + &cd->u.verity.hdr, flags|CRYPT_ACTIVATE_READONLY); if (r == -EPERM) { free(cd->u.verity.root_hash); diff --git a/lib/utils_dm.h b/lib/utils_dm.h index cd8b6534..9d79c07b 100644 --- a/lib/utils_dm.h +++ b/lib/utils_dm.h @@ -3,8 +3,8 @@ * * Copyright (C) 2004, Jana Saout * Copyright (C) 2004-2007, Clemens Fruhwirth - * Copyright (C) 2009-2015, Red Hat, Inc. All rights reserved. - * Copyright (C) 2009-2015, Milan Broz + * Copyright (C) 2009-2016, Red Hat, Inc. All rights reserved. + * Copyright (C) 2009-2016, Milan Broz * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -42,6 +42,7 @@ struct device; #define DM_TCW_SUPPORTED (1 << 6) /* tcw (TCRYPT CBC with whitening) */ #define DM_SAME_CPU_CRYPT_SUPPORTED (1 << 7) /* same_cpu_crypt */ #define DM_SUBMIT_FROM_CRYPT_CPUS_SUPPORTED (1 << 8) /* submit_from_crypt_cpus */ +#define DM_VERITY_ON_CORRUPTION_SUPPORTED (1 << 9) /* ignore/restart_on_corruption, ignore_zero_block */ uint32_t dm_flags(void); diff --git a/man/veritysetup.8 b/man/veritysetup.8 index 15dd9630..4c5168ba 100644 --- a/man/veritysetup.8 +++ b/man/veritysetup.8 @@ -37,7 +37,8 @@ Creates a mapping with backed by device and using The is a hexadecimal string. -\fB\fR can be [\-\-hash-offset, \-\-no-superblock] +\fB\fR can be [\-\-hash-offset, \-\-no-superblock, +\-\-ignore-corruption or \-\-restart-on-corruption, \-\-ignore-zero-blocks] If option \-\-no-superblock is used, you have to use as the same options as in initial format operation. @@ -110,6 +111,24 @@ Use the provided UUID for format command instead of generating new one. The UUID must be provided in standard UUID format, e.g. 12345678-1234-1234-1234-123456789abc. .TP +.B "\-\-ignore-corruption", "\-\-restart-on-corruption" +Defines what to do if data integrity problem is detected (data corruption). + +Without these options kernel fails the IO operation with I/O error. +With \-\-ignore-corruption option the corruption is only logged. +With \-\-restart-on-corruption the kernel is restarted immediatelly. +(You have to provide way how to avoid restart loops.) + +\fBWARNING:\fR Use these options only for very specific cases. +These options are available since Linux kernel version 4.1. +.TP +.B "\-\-ignore-zero-blocks" +Instruct kernel to not verify blocks that are expected to contain zeroes +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 "\-\-version" Show the program version. .SH RETURN CODES @@ -130,9 +149,9 @@ The first implementation of veritysetup was written by Chrome OS authors. This version is based on verification code written by Mikulas Patocka and rewritten for libcryptsetup by Milan Broz . .SH COPYRIGHT -Copyright \(co 2012-2013 Red Hat, Inc. +Copyright \(co 2012-2016 Red Hat, Inc. .br -Copyright \(co 2012-2014 Milan Broz +Copyright \(co 2012-2016 Milan Broz This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. diff --git a/src/veritysetup.c b/src/veritysetup.c index 8f45be4c..70eb0547 100644 --- a/src/veritysetup.c +++ b/src/veritysetup.c @@ -1,8 +1,8 @@ /* * veritysetup - setup cryptographic volumes for dm-verity * - * Copyright (C) 2012-2013, Red Hat, Inc. All rights reserved. - * Copyright (C) 2012-2013, Milan Broz + * Copyright (C) 2012-2016, Red Hat, Inc. All rights reserved. + * Copyright (C) 2012-2016, Milan Broz * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -33,6 +33,9 @@ static uint64_t data_blocks = 0; static const char *salt_string = NULL; static uint64_t hash_offset = 0; 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_version_mode = 0; @@ -127,6 +130,13 @@ static int _activate(const char *dm_device, if ((r = crypt_init(&cd, hash_device))) goto out; + if (opt_ignore_corruption) + activate_flags |= CRYPT_ACTIVATE_IGNORE_CORRUPTION; + if (opt_restart_on_corruption) + activate_flags |= CRYPT_ACTIVATE_RESTART_ON_CORRUPTION; + if (opt_ignore_zero_blocks) + activate_flags |= CRYPT_ACTIVATE_IGNORE_ZERO_BLOCKS; + if (use_superblock) { params.flags = flags; params.hash_area_offset = hash_offset; @@ -273,6 +283,14 @@ static int action_status(int arg) } log_std(" hash offset: %" PRIu64 " sectors\n", vp.hash_area_offset * vp.hash_block_size / 512); + + if (cad.flags & (CRYPT_ACTIVATE_IGNORE_CORRUPTION| + CRYPT_ACTIVATE_RESTART_ON_CORRUPTION| + CRYPT_ACTIVATE_IGNORE_ZERO_BLOCKS)) + log_std(" flags: %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" : ""); } out: crypt_free(cd); @@ -383,6 +401,9 @@ int main(int argc, const char **argv) { "hash", 'h', POPT_ARG_STRING, &hash_algorithm, 0, N_("Hash algorithm"), N_("string") }, { "salt", 's', POPT_ARG_STRING, &salt_string, 0, N_("Salt"), N_("hex string") }, { "uuid", '\0', POPT_ARG_STRING, &opt_uuid, 0, N_("UUID for device to use."), NULL }, + { "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 }, POPT_TABLEEND }; @@ -468,6 +489,16 @@ int main(int argc, const char **argv) poptGetInvocationName(popt_context)); } + if ((opt_ignore_corruption || opt_restart_on_corruption || opt_ignore_zero_blocks) && strcmp(aname, "create")) + usage(popt_context, EXIT_FAILURE, + _("Option --ignore-corruption, --restart-on-corruption or --ignore-zero-blocks is allowed only for create operation.\n"), + poptGetInvocationName(popt_context)); + + if (opt_ignore_corruption && opt_restart_on_corruption) + usage(popt_context, EXIT_FAILURE, + _("Option --ignore-corruption and --restart-on-corruption cannot be used together.\n"), + poptGetInvocationName(popt_context)); + if (opt_debug) { opt_verbose = 1; crypt_set_debug_level(-1); diff --git a/tests/verity-compat-test b/tests/verity-compat-test index 56a8f0bf..cb90edaf 100755 --- a/tests/verity-compat-test +++ b/tests/verity-compat-test @@ -55,6 +55,18 @@ function check_exists() [ -b /dev/mapper/$DEV_NAME ] || fail } +function check_version() +{ + VER_STR=$(dmsetup targets | grep verity | cut -f 3 -dv) + 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 + return 1 +} + function compare_out() # $1 what, $2 expected { OPT=$(grep -v "^#" $DEV_OUT | grep -i "$1" | sed -e s/.*\:\ // ) @@ -132,6 +144,21 @@ function check_root_hash() # $1 size, $2 hash, $3 salt, $4 version, $5 hash, [$6 done } +function check_option() # $1 size, $2 hash, $3 salt, $4 version, $5 hash, $6 CLI option, $7 status option +{ + DEV_PARAMS="$LOOPDEV1 $LOOPDEV2" + FORMAT_PARAMS="--format=$4 --data-block-size=$1 --hash-block-size=$1 --hash=$5 --salt=$3" + + echo -n "Option $6 " + $VERITYSETUP format $DEV_PARAMS $FORMAT_PARAMS >/dev/null 2>&1 || fail + $VERITYSETUP create $DEV_NAME $DEV_PARAMS $2 $6 >/dev/null 2>&1 || fail + check_exists + $VERITYSETUP status $DEV_NAME 2>/dev/null | grep flags | grep -q $7 || fail + dmsetup table $DEV_NAME 2>/dev/null | grep -q $7 || fail + $VERITYSETUP remove $DEV_NAME >/dev/null 2>&1 || fail + echo "[OK]" +} + function valgrind_setup() { which valgrind >/dev/null 2>&1 || fail "Cannot find valgrind." @@ -181,5 +208,16 @@ 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 + echo "Verity data corruption options test." + SALT=e48da609055204e89ae53b655ca2216dd983cf3cb829f34f63a297d106d53e2d + HASH=9de18652fe74edfb9b805aaed72ae2aa48f94333f1ba5c452ac33b1c39325174 + prepare 8192 1024 + check_option 512 $HASH $SALT 1 sha256 "--ignore-corruption" "ignore_corruption" + 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" +fi + remove_mapping exit 0