mirror of
https://gitlab.com/cryptsetup/cryptsetup.git
synced 2025-12-13 03:40:05 +01:00
veritysetup: add --root-hash-file option
Allow to pass the root hash via a file, rather than verbatim on the command line, for the open/verify/format actions. It is much more convenient when using veritysetup in scripts. [some modifications by mbroz:] - Add additional syntax and option description to man page. - Fix a segfault with non-existing path. - Do not read full file. - Small refactor for argc handling and option processing.
This commit is contained in:
committed by
Milan Broz
parent
06f132066b
commit
cc374ee10d
@@ -28,10 +28,15 @@ If hash device path doesn't exist, it will be created as file.
|
||||
|
||||
\fB<options>\fR can be [\-\-hash, \-\-no-superblock, \-\-format,
|
||||
\-\-data-block-size, \-\-hash-block-size, \-\-data-blocks, \-\-hash-offset,
|
||||
\-\-salt, \-\-uuid]
|
||||
\-\-salt, \-\-uuid, \-\-root-hash-file]
|
||||
|
||||
If option \-\-root-hash-file is used, the root hash is stored in hex-encoded text
|
||||
format in <path>.
|
||||
.PP
|
||||
\fIopen\fR <data_device> <name> <hash_device> <root_hash>
|
||||
.br
|
||||
\fIopen\fR <data_device> <name> <hash_device> \-\-root-hash-file <path>
|
||||
.br
|
||||
\fIcreate\fR <name> <data_device> <hash_device> <root_hash> (\fBOBSOLETE syntax\fR)
|
||||
.IP
|
||||
Creates a mapping with <name> backed by device <data_device> and using
|
||||
@@ -41,12 +46,19 @@ The <root_hash> is a hexadecimal string.
|
||||
|
||||
\fB<options>\fR can be [\-\-hash-offset, \-\-no-superblock,
|
||||
\-\-ignore-corruption or \-\-restart-on-corruption, \-\-panic-on-corruption,
|
||||
\-\-ignore-zero-blocks, \-\-check-at-most-once, \-\-root-hash-signature]
|
||||
\-\-ignore-zero-blocks, \-\-check-at-most-once, \-\-root-hash-signature,
|
||||
\-\-root-hash-file]
|
||||
|
||||
If option \-\-root-hash-file is used, the root hash is read from <path> instead
|
||||
of from the command line parameter. Expects hex-encoded text, without terminating
|
||||
newline.
|
||||
|
||||
If option \-\-no-superblock is used, you have to use as the same options
|
||||
as in initial format operation.
|
||||
.PP
|
||||
\fIverify\fR <data_device> <hash_device> <root_hash>
|
||||
.br
|
||||
\fIverify\fR <data_device> <hash_device> \-\-root-hash-file <path>
|
||||
.IP
|
||||
Verifies data on data_device with use of hash blocks stored on hash_device.
|
||||
|
||||
@@ -54,7 +66,11 @@ This command performs userspace verification, no kernel device is created.
|
||||
|
||||
The <root_hash> is a hexadecimal string.
|
||||
|
||||
\fB<options>\fR can be [\-\-hash-offset, \-\-no-superblock]
|
||||
If option \-\-root-hash-file is used, the root hash is read from <path> instead
|
||||
of from the command line parameter. Expects hex-encoded text, without terminating
|
||||
newline.
|
||||
|
||||
\fB<options>\fR can be [\-\-hash-offset, \-\-no-superblock, \-\-root-hash-file]
|
||||
|
||||
If option \-\-no-superblock is used, you have to use as the same options
|
||||
as in initial format operation.
|
||||
@@ -181,6 +197,9 @@ This is the offset, in bytes, from the start of the FEC device to the beginning
|
||||
Number of generator roots. This equals to the number of parity bytes in the encoding data.
|
||||
In RS(M, N) encoding, the number of roots is M-N. M is 255 and M-N is between 2 and 24 (including).
|
||||
.TP
|
||||
.B "\-\-root-hash-file=FILE"
|
||||
Path to file with stored root hash in hex-encoded text.
|
||||
.TP
|
||||
.B "\-\-root-hash-signature=FILE"
|
||||
Path to roothash signature file used to verify the root hash (in kernel).
|
||||
This feature requires Linux kernel version 5.4 or more recent.
|
||||
@@ -207,9 +226,10 @@ Error codes are:
|
||||
Calculates and stores verification data on hash_device for the first 256 blocks (of block-size).
|
||||
If hash_device does not exist, it is created (as file image).
|
||||
|
||||
.B "veritysetup format <data_device> <hash_device>"
|
||||
.B "veritysetup format --root-hash-file <path> <data_device> <hash_device>"
|
||||
|
||||
Calculates and stores verification data on hash_device for the whole data_device.
|
||||
Calculates and stores verification data on hash_device for the whole data_device, and store the
|
||||
root hash as hex-encoded text in <path>.
|
||||
|
||||
.B "veritysetup \-\-data-blocks=256 \-\-hash-offset=1052672 format <device> <device>"
|
||||
|
||||
@@ -225,6 +245,10 @@ as in the format command. The <root_hash> was calculated in format command.
|
||||
|
||||
Verifies device without activation (in userspace).
|
||||
|
||||
.B "veritysetup \-\-data-blocks=256 \-\-hash-offset=1052672 --root-hash-file <path> verify <data_device> <hash_device>"
|
||||
|
||||
Verifies device without activation (in userspace). Root hash passed via a file rather than inline.
|
||||
|
||||
.B "veritysetup \-\-fec-device=<fec_device> \-\-fec-roots=10 format <data_device> <hash_device>"
|
||||
|
||||
Calculates and stores verification and encoding data for data_device.
|
||||
|
||||
@@ -126,6 +126,7 @@
|
||||
#define OPT_RESILIENCE_HASH "resilience-hash"
|
||||
#define OPT_RESTART_ON_CORRUPTION "restart-on-corruption"
|
||||
#define OPT_RESUME_ONLY "resume-only"
|
||||
#define OPT_ROOT_HASH_FILE "root-hash-file"
|
||||
#define OPT_ROOT_HASH_SIGNATURE "root-hash-signature"
|
||||
#define OPT_SALT "salt"
|
||||
#define OPT_SECTOR_SIZE "sector-size"
|
||||
|
||||
@@ -77,7 +77,9 @@ static int action_format(void)
|
||||
struct crypt_device *cd = NULL;
|
||||
struct crypt_params_verity params = {};
|
||||
uint32_t flags = CRYPT_VERITY_CREATE_HASH;
|
||||
int r;
|
||||
char *root_hash_bytes = NULL;
|
||||
size_t root_hash_size;
|
||||
int root_hash_fd = -1, i, r;
|
||||
|
||||
/* Try to create hash image if doesn't exist */
|
||||
r = open(action_argv[1], O_WRONLY | O_EXCL | O_CREAT, S_IRUSR | S_IWUSR);
|
||||
@@ -111,11 +113,46 @@ static int action_format(void)
|
||||
goto out;
|
||||
|
||||
r = crypt_format(cd, CRYPT_VERITY, NULL, NULL, ARG_STR(OPT_UUID_ID), NULL, 0, ¶ms);
|
||||
if (!r)
|
||||
crypt_dump(cd);
|
||||
if (r < 0)
|
||||
goto out;
|
||||
|
||||
crypt_dump(cd);
|
||||
|
||||
/* Create or overwrite the root hash file */
|
||||
if (ARG_SET(OPT_ROOT_HASH_FILE_ID)) {
|
||||
root_hash_size = crypt_get_volume_key_size(cd);
|
||||
root_hash_bytes = malloc(root_hash_size);
|
||||
if (!root_hash_bytes) {
|
||||
r = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
|
||||
r = crypt_volume_key_get(cd, CRYPT_ANY_SLOT, root_hash_bytes, &root_hash_size, NULL, 0);
|
||||
if (r < 0)
|
||||
goto out;
|
||||
|
||||
root_hash_fd = open(ARG_STR(OPT_ROOT_HASH_FILE_ID), O_WRONLY | O_TRUNC | O_CREAT, S_IRUSR | S_IWUSR);
|
||||
if (root_hash_fd == -1) {
|
||||
log_err(_("Cannot create root hash file %s for writing."), ARG_STR(OPT_ROOT_HASH_FILE_ID));
|
||||
r = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
for (i = 0; i < (int)root_hash_size; i++)
|
||||
if (dprintf(root_hash_fd, "%02hhx", root_hash_bytes[i]) != 2) {
|
||||
log_err(_("Cannot write to root hash file %s."), ARG_STR(OPT_ROOT_HASH_FILE_ID));
|
||||
r = -EIO;
|
||||
goto out;
|
||||
}
|
||||
|
||||
log_dbg("Created root hash file %s.", ARG_STR(OPT_ROOT_HASH_FILE_ID));
|
||||
}
|
||||
out:
|
||||
crypt_free(cd);
|
||||
free(CONST_CAST(char*)params.salt);
|
||||
free(root_hash_bytes);
|
||||
if (root_hash_fd != -1)
|
||||
close(root_hash_fd);
|
||||
return r;
|
||||
}
|
||||
|
||||
@@ -128,11 +165,11 @@ static int _activate(const char *dm_device,
|
||||
struct crypt_device *cd = NULL;
|
||||
struct crypt_params_verity params = {};
|
||||
uint32_t activate_flags = CRYPT_ACTIVATE_READONLY;
|
||||
char *root_hash_bytes = NULL;
|
||||
ssize_t hash_size;
|
||||
char *root_hash_bytes = NULL, *root_hash_from_file = NULL;
|
||||
ssize_t hash_size, hash_size_hex;
|
||||
struct stat st;
|
||||
char *signature = NULL;
|
||||
int signature_size = 0, r;
|
||||
int signature_size = 0, root_hash_fd = -1, r;
|
||||
|
||||
if ((r = crypt_init_data_device(&cd, hash_device, data_device)))
|
||||
goto out;
|
||||
@@ -165,6 +202,36 @@ static int _activate(const char *dm_device,
|
||||
goto out;
|
||||
|
||||
hash_size = crypt_get_volume_key_size(cd);
|
||||
hash_size_hex = 2 * hash_size;
|
||||
|
||||
if (!root_hash) {
|
||||
root_hash_fd = open(ARG_STR(OPT_ROOT_HASH_FILE_ID), O_RDONLY);
|
||||
if (root_hash_fd == -1) {
|
||||
log_err(_("Cannot read root hash file %s."), ARG_STR(OPT_ROOT_HASH_FILE_ID));
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (fstat(root_hash_fd, &st) || !S_ISREG(st.st_mode) || st.st_size < hash_size_hex) {
|
||||
log_err(_("Invalid root hash file %s."), ARG_STR(OPT_ROOT_HASH_FILE_ID));
|
||||
r = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
root_hash_from_file = malloc(hash_size_hex + 1);
|
||||
if (!root_hash_from_file) {
|
||||
r = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (read_buffer(root_hash_fd, root_hash_from_file, hash_size_hex) != hash_size_hex) {
|
||||
log_err(_("Cannot read root hash file %s."), root_hash_from_file);
|
||||
goto out;
|
||||
}
|
||||
|
||||
root_hash_from_file[hash_size_hex] = '\0';
|
||||
root_hash = root_hash_from_file;
|
||||
}
|
||||
|
||||
if (crypt_hex_to_bytes(root_hash, &root_hash_bytes, 0) != hash_size) {
|
||||
log_err(_("Invalid root hash string specified."));
|
||||
r = -EINVAL;
|
||||
@@ -193,26 +260,39 @@ static int _activate(const char *dm_device,
|
||||
out:
|
||||
crypt_safe_free(signature);
|
||||
crypt_free(cd);
|
||||
free(root_hash_from_file);
|
||||
free(root_hash_bytes);
|
||||
free(CONST_CAST(char*)params.salt);
|
||||
if (root_hash_fd != -1)
|
||||
close(root_hash_fd);
|
||||
return r;
|
||||
}
|
||||
|
||||
static int action_open(void)
|
||||
{
|
||||
if (action_argc < 4 && !ARG_SET(OPT_ROOT_HASH_FILE_ID)) {
|
||||
log_err(_("Command requires <root_hash> or --root-hash-file option as argument."));
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return _activate(action_argv[1],
|
||||
action_argv[0],
|
||||
action_argv[2],
|
||||
action_argv[3],
|
||||
ARG_SET(OPT_ROOT_HASH_FILE_ID) ? NULL : action_argv[3],
|
||||
ARG_SET(OPT_ROOT_HASH_SIGNATURE_ID) ? CRYPT_VERITY_ROOT_HASH_SIGNATURE : 0);
|
||||
}
|
||||
|
||||
static int action_verify(void)
|
||||
{
|
||||
if (action_argc < 3 && !ARG_SET(OPT_ROOT_HASH_FILE_ID)) {
|
||||
log_err(_("Command requires <root_hash> or --root-hash-file option as argument."));
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return _activate(NULL,
|
||||
action_argv[0],
|
||||
action_argv[1],
|
||||
action_argv[2],
|
||||
ARG_SET(OPT_ROOT_HASH_FILE_ID) ? NULL : action_argv[2],
|
||||
CRYPT_VERITY_CHECK_HASH);
|
||||
}
|
||||
|
||||
@@ -396,8 +476,8 @@ static struct action_type {
|
||||
const char *desc;
|
||||
} action_types[] = {
|
||||
{ "format", action_format, 2, N_("<data_device> <hash_device>"),N_("format device") },
|
||||
{ "verify", action_verify, 3, N_("<data_device> <hash_device> <root_hash>"),N_("verify device") },
|
||||
{ "open", action_open, 4, N_("<data_device> <name> <hash_device> <root_hash>"),N_("open device as <name>") },
|
||||
{ "verify", action_verify, 2, N_("<data_device> <hash_device> [<root_hash>]"),N_("verify device") },
|
||||
{ "open", action_open, 3, N_("<data_device> <name> <hash_device> [<root_hash>]"),N_("open device as <name>") },
|
||||
{ "close", action_close, 1, N_("<name>"),N_("close device (remove mapping)") },
|
||||
{ "status", action_status, 1, N_("<name>"),N_("show active device status") },
|
||||
{ "dump", action_dump, 1, N_("<hash_device>"),N_("show on-disk information") },
|
||||
|
||||
@@ -57,6 +57,8 @@ ARG(OPT_PANIC_ON_CORRUPTION, '\0', POPT_ARG_NONE, N_("Panic kernel if corruption
|
||||
|
||||
ARG(OPT_RESTART_ON_CORRUPTION, '\0', POPT_ARG_NONE, N_("Restart kernel if corruption is detected"), NULL, CRYPT_ARG_BOOL, {}, OPT_RESTART_ON_CORRUPTION_ACTIONS)
|
||||
|
||||
ARG(OPT_ROOT_HASH_FILE, '\0', POPT_ARG_STRING, N_("Path to root hash file"), NULL, CRYPT_ARG_STRING, {}, OPT_ROOT_HASH_FILE_ACTIONS)
|
||||
|
||||
ARG(OPT_ROOT_HASH_SIGNATURE, '\0', POPT_ARG_STRING, N_("Path to root hash signature file"), NULL, CRYPT_ARG_STRING, {}, OPT_ROOT_HASH_SIGNATURE_ACTIONS)
|
||||
|
||||
ARG(OPT_SALT, 's', POPT_ARG_STRING, N_("Salt"), N_("hex string"), CRYPT_ARG_STRING, {}, {})
|
||||
|
||||
@@ -37,6 +37,7 @@
|
||||
#define OPT_IGNORE_ZERO_BLOCKS_ACTIONS { OPEN_ACTION }
|
||||
#define OPT_RESTART_ON_CORRUPTION_ACTIONS { OPEN_ACTION }
|
||||
#define OPT_PANIC_ON_CORRUPTION_ACTIONS { OPEN_ACTION }
|
||||
#define OPT_ROOT_HASH_FILE_ACTIONS { FORMAT_ACTION, OPEN_ACTION, VERIFY_ACTION }
|
||||
#define OPT_ROOT_HASH_SIGNATURE_ACTIONS { OPEN_ACTION }
|
||||
|
||||
enum {
|
||||
|
||||
@@ -21,7 +21,7 @@ function remove_mapping()
|
||||
[ -b /dev/mapper/$DEV_NAME2 ] && dmsetup remove $DEV_NAME2 >/dev/null 2>&1
|
||||
[ -b /dev/mapper/$DEV_NAME ] && dmsetup remove $DEV_NAME >/dev/null 2>&1
|
||||
[ ! -z "$LOOPDEV1" ] && losetup -d $LOOPDEV1 >/dev/null 2>&1
|
||||
rm -f $IMG $IMG_HASH $DEV_OUT $FEC_DEV $IMG_TMP >/dev/null 2>&1
|
||||
rm -f $IMG $IMG.roothash $IMG_HASH $DEV_OUT $FEC_DEV $IMG_TMP >/dev/null 2>&1
|
||||
LOOPDEV1=""
|
||||
LOOPDEV2=""
|
||||
}
|
||||
@@ -112,6 +112,10 @@ function check_root_hash_fail()
|
||||
|
||||
function check_root_hash() # $1 size, $2 hash, $3 salt, $4 version, $5 hash, [$6 offset]
|
||||
{
|
||||
local FORMAT_PARAMS
|
||||
local VERIFY_PARAMS
|
||||
local ROOT_HASH
|
||||
|
||||
if [ -z "$LOOPDEV2" ] ; then
|
||||
BLOCKS=$(($6 / $1))
|
||||
DEV_PARAMS="$LOOPDEV1 $LOOPDEV1 \
|
||||
@@ -121,6 +125,7 @@ function check_root_hash() # $1 size, $2 hash, $3 salt, $4 version, $5 hash, [$6
|
||||
DEV_PARAMS="$LOOPDEV1 $LOOPDEV2"
|
||||
fi
|
||||
|
||||
for root_hash_as_file in yes no; do
|
||||
for sb in yes no; do
|
||||
FORMAT_PARAMS="--format=$4 --data-block-size=$1 --hash-block-size=$1 --hash=$5 --salt=$3"
|
||||
if [ $sb == yes ] ; then
|
||||
@@ -129,20 +134,28 @@ function check_root_hash() # $1 size, $2 hash, $3 salt, $4 version, $5 hash, [$6
|
||||
FORMAT_PARAMS="$FORMAT_PARAMS --no-superblock"
|
||||
VERIFY_PARAMS=$FORMAT_PARAMS
|
||||
fi
|
||||
if [ $root_hash_as_file == yes ] ; then
|
||||
echo -n $2 > $IMG.roothash
|
||||
FORMAT_PARAMS="$FORMAT_PARAMS --root-hash-file=$IMG.roothash"
|
||||
VERIFY_PARAMS="$VERIFY_PARAMS --root-hash-file=$IMG.roothash"
|
||||
ROOT_HASH=""
|
||||
else
|
||||
ROOT_HASH="$2"
|
||||
fi
|
||||
|
||||
for fail in data hash; do
|
||||
wipe
|
||||
echo -n "V$4(sb=$sb) $5 block size $1: "
|
||||
echo -n "V$4(sb=$sb root_hash_as_file=$root_hash_as_file) $5 block size $1: "
|
||||
$VERITYSETUP format $DEV_PARAMS $FORMAT_PARAMS >$DEV_OUT || fail
|
||||
|
||||
echo -n "[root hash]"
|
||||
compare_out "root hash" $2
|
||||
compare_out "salt" "$3"
|
||||
|
||||
$VERITYSETUP verify $DEV_PARAMS $VERIFY_PARAMS $2 >>$DEV_OUT 2>&1 || fail
|
||||
$VERITYSETUP verify $DEV_PARAMS $VERIFY_PARAMS $ROOT_HASH >>$DEV_OUT 2>&1 || fail
|
||||
echo -n "[verify]"
|
||||
|
||||
$VERITYSETUP create $DEV_NAME $DEV_PARAMS $VERIFY_PARAMS $2 >>$DEV_OUT 2>&1 || fail
|
||||
$VERITYSETUP create $DEV_NAME $DEV_PARAMS $VERIFY_PARAMS $ROOT_HASH >>$DEV_OUT 2>&1 || fail
|
||||
check_exists
|
||||
echo -n "[activate]"
|
||||
|
||||
@@ -167,9 +180,9 @@ function check_root_hash() # $1 size, $2 hash, $3 salt, $4 version, $5 hash, [$6
|
||||
;;
|
||||
esac
|
||||
|
||||
$VERITYSETUP verify $DEV_PARAMS $VERIFY_PARAMS $2 >>$DEV_OUT 2>&1 && \
|
||||
$VERITYSETUP verify $DEV_PARAMS $VERIFY_PARAMS $ROOT_HASH >>$DEV_OUT 2>&1 && \
|
||||
fail "userspace check for $TXT corruption"
|
||||
$VERITYSETUP create $DEV_NAME $DEV_PARAMS $VERIFY_PARAMS $2 >>$DEV_OUT 2>&1 || \
|
||||
$VERITYSETUP create $DEV_NAME $DEV_PARAMS $VERIFY_PARAMS $ROOT_HASH >>$DEV_OUT 2>&1 || \
|
||||
fail "activation"
|
||||
dd if=/dev/mapper/$DEV_NAME of=/dev/null bs=$1 2>/dev/null
|
||||
dmsetup status $DEV_NAME | grep "verity V" >/dev/null && \
|
||||
@@ -178,6 +191,7 @@ function check_root_hash() # $1 size, $2 hash, $3 salt, $4 version, $5 hash, [$6
|
||||
echo "[$TXT corruption]"
|
||||
done
|
||||
done
|
||||
done
|
||||
}
|
||||
|
||||
function corrupt_device() # $1 device, $2 device_size(in bytes), $3 #{corrupted_bytes}
|
||||
|
||||
Reference in New Issue
Block a user