dissect/nspawn: add support for dm-verity root hash signature

Since cryptsetup 2.3.0 a new API to verify dm-verity volumes by a
pkcs7 signature, with the public key in the kernel keyring,
is available. Use it if libcryptsetup supports it.
This commit is contained in:
Luca Boccassi 2020-06-02 15:35:58 +01:00
parent 035e8e50d7
commit c2923fdcd7
9 changed files with 176 additions and 26 deletions

6
README
View File

@ -35,6 +35,7 @@ LICENSE:
REQUIREMENTS: REQUIREMENTS:
Linux kernel >= 3.13 Linux kernel >= 3.13
Linux kernel >= 4.2 for unified cgroup hierarchy support Linux kernel >= 4.2 for unified cgroup hierarchy support
Linux kernel >= 5.4 for signed Verity images support
Kernel Config Options: Kernel Config Options:
CONFIG_DEVTMPFS CONFIG_DEVTMPFS
@ -102,6 +103,9 @@ REQUIREMENTS:
CONFIG_EFIVAR_FS CONFIG_EFIVAR_FS
CONFIG_EFI_PARTITION CONFIG_EFI_PARTITION
Required for signed Verity images support:
CONFIG_DM_VERITY_VERIFY_ROOTHASH_SIG
We recommend to turn off Real-Time group scheduling in the We recommend to turn off Real-Time group scheduling in the
kernel when using systemd. RT group scheduling effectively kernel when using systemd. RT group scheduling effectively
makes RT scheduling unavailable for most userspace, since it makes RT scheduling unavailable for most userspace, since it
@ -144,7 +148,7 @@ REQUIREMENTS:
libblkid >= 2.24 (from util-linux) (optional) libblkid >= 2.24 (from util-linux) (optional)
libkmod >= 15 (optional) libkmod >= 15 (optional)
PAM >= 1.1.2 (optional) PAM >= 1.1.2 (optional)
libcryptsetup (optional) libcryptsetup (optional), >= 2.3.0 required for signed Verity images support
libaudit (optional) libaudit (optional)
libacl (optional) libacl (optional)
libselinux (optional) libselinux (optional)

View File

@ -304,7 +304,7 @@
<para>Single file system images (i.e. file systems without a surrounding partition table) can be opened using <para>Single file system images (i.e. file systems without a surrounding partition table) can be opened using
dm-verity if the integrity data is passed using the <option>--root-hash=</option> and dm-verity if the integrity data is passed using the <option>--root-hash=</option> and
<option>--verity-data=</option> options.</para> <option>--verity-data=</option> (and optionally <option>--root-hash-sig=</option>) options.</para>
<para>Any other partitions, such as foreign partitions or swap partitions are not mounted. May not be specified <para>Any other partitions, such as foreign partitions or swap partitions are not mounted. May not be specified
together with <option>--directory=</option>, <option>--template=</option>.</para></listitem> together with <option>--directory=</option>, <option>--template=</option>.</para></listitem>
@ -399,6 +399,18 @@
is read from it and automatically used, also as formatted hexadecimal characters.</para></listitem> is read from it and automatically used, also as formatted hexadecimal characters.</para></listitem>
</varlistentry> </varlistentry>
<varlistentry>
<term><option>--root-hash-sig=</option></term>
<listitem><para>Takes a PKCS7 formatted binary signature of the <option>--root-hash=</option> option as a path
to a DER encoded signature file or as an ASCII base64 string encoding of the DER encoded signature, prefixed
by <literal>base64:</literal>. The dm-verity volume will only be opened if the signature of the root hash hex
string is valid and done by a public key present in the kernel keyring. If this option is not specified, but a
file with the <filename>.roothash.p7s</filename> suffix is found next to the image file, bearing otherwise the
same name (except if the image has the <filename>.raw</filename> suffix, in which case the signature file must
not have it in its name), the signature is read from it and automatically used.</para></listitem>
</varlistentry>
<varlistentry> <varlistentry>
<term><option>--verity-data=</option></term> <term><option>--verity-data=</option></term>

View File

@ -1035,6 +1035,8 @@ if want_libcryptsetup != 'false' and not skip_deps
conf.set10('HAVE_CRYPT_SET_METADATA_SIZE', conf.set10('HAVE_CRYPT_SET_METADATA_SIZE',
have and cc.has_function('crypt_set_metadata_size', dependencies : libcryptsetup)) have and cc.has_function('crypt_set_metadata_size', dependencies : libcryptsetup))
conf.set10('HAVE_CRYPT_ACTIVATE_BY_SIGNED_KEY',
have and cc.has_function('crypt_activate_by_signed_key', dependencies : libcryptsetup))
else else
have = false have = false
libcryptsetup = [] libcryptsetup = []

View File

@ -71,7 +71,7 @@ _systemd_nspawn() {
--pivot-root --property --private-users --network-namespace-path --network-ipvlan --pivot-root --property --private-users --network-namespace-path --network-ipvlan
--network-veth-extra --network-zone -p --port --system-call-filter --overlay --overlay-ro --network-veth-extra --network-zone -p --port --system-call-filter --overlay --overlay-ro
--settings --rlimit --hostname --no-new-privileges --oom-score-adjust --cpu-affinity --settings --rlimit --hostname --no-new-privileges --oom-score-adjust --cpu-affinity
--resolv-conf --timezone' --resolv-conf --timezone --root-hash-sig'
) )
_init_completion || return _init_completion || return
@ -183,6 +183,10 @@ _systemd_nspawn() {
--timezone) --timezone)
comps=$( systemd-nspawn --timezone=help 2>/dev/null ) comps=$( systemd-nspawn --timezone=help 2>/dev/null )
;; ;;
--root-hash-sig)
compopt -o nospace
comps=$( compgen -A file -- "$cur" )
;;
esac esac
COMPREPLY=( $(compgen -W '$comps' -- "$cur") ) COMPREPLY=( $(compgen -W '$comps' -- "$cur") )
return 0 return 0

View File

@ -1268,7 +1268,7 @@ int setup_namespace(
_cleanup_(decrypted_image_unrefp) DecryptedImage *decrypted_image = NULL; _cleanup_(decrypted_image_unrefp) DecryptedImage *decrypted_image = NULL;
_cleanup_(dissected_image_unrefp) DissectedImage *dissected_image = NULL; _cleanup_(dissected_image_unrefp) DissectedImage *dissected_image = NULL;
_cleanup_free_ void *root_hash_decoded = NULL; _cleanup_free_ void *root_hash_decoded = NULL;
_cleanup_free_ char *verity_data = NULL; _cleanup_free_ char *verity_data = NULL, *hash_sig_path = NULL;
MountEntry *m = NULL, *mounts = NULL; MountEntry *m = NULL, *mounts = NULL;
size_t n_mounts; size_t n_mounts;
bool require_prefix = false; bool require_prefix = false;
@ -1299,7 +1299,7 @@ int setup_namespace(
if (r < 0) if (r < 0)
return log_debug_errno(r, "Failed to create loop device for root image: %m"); return log_debug_errno(r, "Failed to create loop device for root image: %m");
r = verity_metadata_load(root_image, root_hash_path, root_hash ? NULL : &root_hash_decoded, root_hash ? NULL : &root_hash_size, root_verity ? NULL : &verity_data); r = verity_metadata_load(root_image, root_hash_path, root_hash ? NULL : &root_hash_decoded, root_hash ? NULL : &root_hash_size, root_verity ? NULL : &verity_data, &hash_sig_path);
if (r < 0) if (r < 0)
return log_debug_errno(r, "Failed to load root hash: %m"); return log_debug_errno(r, "Failed to load root hash: %m");
dissect_image_flags |= root_verity || verity_data ? DISSECT_IMAGE_NO_PARTITION_TABLE : 0; dissect_image_flags |= root_verity || verity_data ? DISSECT_IMAGE_NO_PARTITION_TABLE : 0;
@ -1308,7 +1308,7 @@ int setup_namespace(
if (r < 0) if (r < 0)
return log_debug_errno(r, "Failed to dissect image: %m"); return log_debug_errno(r, "Failed to dissect image: %m");
r = dissected_image_decrypt(dissected_image, NULL, root_hash ?: root_hash_decoded, root_hash_size, root_verity ?: verity_data, dissect_image_flags, &decrypted_image); r = dissected_image_decrypt(dissected_image, NULL, root_hash ?: root_hash_decoded, root_hash_size, root_verity ?: verity_data, hash_sig_path, NULL, 0, dissect_image_flags, &decrypted_image);
if (r < 0) if (r < 0)
return log_debug_errno(r, "Failed to decrypt dissected image: %m"); return log_debug_errno(r, "Failed to decrypt dissected image: %m");
} }

View File

@ -28,9 +28,14 @@ static DissectImageFlags arg_flags = DISSECT_IMAGE_REQUIRE_ROOT|DISSECT_IMAGE_DI
static void *arg_root_hash = NULL; static void *arg_root_hash = NULL;
static char *arg_verity_data = NULL; static char *arg_verity_data = NULL;
static size_t arg_root_hash_size = 0; static size_t arg_root_hash_size = 0;
static char *arg_root_hash_sig_path = NULL;
static void *arg_root_hash_sig = NULL;
static size_t arg_root_hash_sig_size = 0;
STATIC_DESTRUCTOR_REGISTER(arg_root_hash, freep); STATIC_DESTRUCTOR_REGISTER(arg_root_hash, freep);
STATIC_DESTRUCTOR_REGISTER(arg_verity_data, freep); STATIC_DESTRUCTOR_REGISTER(arg_verity_data, freep);
STATIC_DESTRUCTOR_REGISTER(arg_root_hash_sig_path, freep);
STATIC_DESTRUCTOR_REGISTER(arg_root_hash_sig, freep);
static void help(void) { static void help(void) {
printf("%s [OPTIONS...] IMAGE\n" printf("%s [OPTIONS...] IMAGE\n"
@ -43,6 +48,10 @@ static void help(void) {
" --fsck=BOOL Run fsck before mounting\n" " --fsck=BOOL Run fsck before mounting\n"
" --discard=MODE Choose 'discard' mode (disabled, loop, all, crypto)\n" " --discard=MODE Choose 'discard' mode (disabled, loop, all, crypto)\n"
" --root-hash=HASH Specify root hash for verity\n" " --root-hash=HASH Specify root hash for verity\n"
" --root-hash-sig=SIG Specify pkcs7 signature of root hash for verity\n"
" as a DER encoded PKCS7, either as a path to a file\n"
" or as an ASCII base64 encoded string prefixed by\n"
" 'base64:'\n"
" --verity-data=PATH Specify data file with hash tree for verity if it is\n" " --verity-data=PATH Specify data file with hash tree for verity if it is\n"
" not embedded in IMAGE\n", " not embedded in IMAGE\n",
program_invocation_short_name, program_invocation_short_name,
@ -57,17 +66,19 @@ static int parse_argv(int argc, char *argv[]) {
ARG_ROOT_HASH, ARG_ROOT_HASH,
ARG_FSCK, ARG_FSCK,
ARG_VERITY_DATA, ARG_VERITY_DATA,
ARG_ROOT_HASH_SIG,
}; };
static const struct option options[] = { static const struct option options[] = {
{ "help", no_argument, NULL, 'h' }, { "help", no_argument, NULL, 'h' },
{ "version", no_argument, NULL, ARG_VERSION }, { "version", no_argument, NULL, ARG_VERSION },
{ "mount", no_argument, NULL, 'm' }, { "mount", no_argument, NULL, 'm' },
{ "read-only", no_argument, NULL, 'r' }, { "read-only", no_argument, NULL, 'r' },
{ "discard", required_argument, NULL, ARG_DISCARD }, { "discard", required_argument, NULL, ARG_DISCARD },
{ "root-hash", required_argument, NULL, ARG_ROOT_HASH }, { "root-hash", required_argument, NULL, ARG_ROOT_HASH },
{ "fsck", required_argument, NULL, ARG_FSCK }, { "fsck", required_argument, NULL, ARG_FSCK },
{ "verity-data", required_argument, NULL, ARG_VERITY_DATA }, { "verity-data", required_argument, NULL, ARG_VERITY_DATA },
{ "root-hash-sig", required_argument, NULL, ARG_ROOT_HASH_SIG },
{} {}
}; };
@ -140,6 +151,31 @@ static int parse_argv(int argc, char *argv[]) {
return r; return r;
break; break;
case ARG_ROOT_HASH_SIG: {
char *value;
if ((value = startswith(optarg, "base64:"))) {
void *p;
size_t l;
r = unbase64mem(value, strlen(value), &p, &l);
if (r < 0)
return log_error_errno(r, "Failed to parse root hash signature '%s': %m", optarg);
free_and_replace(arg_root_hash_sig, p);
arg_root_hash_sig_size = l;
arg_root_hash_sig_path = mfree(arg_root_hash_sig_path);
} else {
r = parse_path_argument_and_warn(optarg, false, &arg_root_hash_sig_path);
if (r < 0)
return r;
arg_root_hash_sig = mfree(arg_root_hash_sig);
arg_root_hash_sig_size = 0;
}
break;
}
case ARG_FSCK: case ARG_FSCK:
r = parse_boolean(optarg); r = parse_boolean(optarg);
if (r < 0) if (r < 0)
@ -202,7 +238,8 @@ static int run(int argc, char *argv[]) {
return log_error_errno(r, "Failed to set up loopback device: %m"); return log_error_errno(r, "Failed to set up loopback device: %m");
r = verity_metadata_load(arg_image, NULL, arg_root_hash ? NULL : &arg_root_hash, &arg_root_hash_size, r = verity_metadata_load(arg_image, NULL, arg_root_hash ? NULL : &arg_root_hash, &arg_root_hash_size,
arg_verity_data ? NULL : &arg_verity_data); arg_verity_data ? NULL : &arg_verity_data,
arg_root_hash_sig_path || arg_root_hash_sig ? NULL : &arg_root_hash_sig_path);
if (r < 0) if (r < 0)
return log_error_errno(r, "Failed to read verity artefacts for %s: %m", arg_image); return log_error_errno(r, "Failed to read verity artefacts for %s: %m", arg_image);
arg_flags |= arg_verity_data ? DISSECT_IMAGE_NO_PARTITION_TABLE : 0; arg_flags |= arg_verity_data ? DISSECT_IMAGE_NO_PARTITION_TABLE : 0;
@ -279,7 +316,7 @@ static int run(int argc, char *argv[]) {
} }
case ACTION_MOUNT: case ACTION_MOUNT:
r = dissected_image_decrypt_interactively(m, NULL, arg_root_hash, arg_root_hash_size, arg_verity_data, arg_flags, &di); r = dissected_image_decrypt_interactively(m, NULL, arg_root_hash, arg_root_hash_size, arg_verity_data, arg_root_hash_sig_path, arg_root_hash_sig, arg_root_hash_sig_size, arg_flags, &di);
if (r < 0) if (r < 0)
return r; return r;

View File

@ -200,6 +200,9 @@ static unsigned long arg_clone_ns_flags = CLONE_NEWIPC|CLONE_NEWPID|CLONE_NEWUTS
static MountSettingsMask arg_mount_settings = MOUNT_APPLY_APIVFS_RO|MOUNT_APPLY_TMPFS_TMP; static MountSettingsMask arg_mount_settings = MOUNT_APPLY_APIVFS_RO|MOUNT_APPLY_TMPFS_TMP;
static void *arg_root_hash = NULL; static void *arg_root_hash = NULL;
static char *arg_verity_data = NULL; static char *arg_verity_data = NULL;
static char *arg_root_hash_sig_path = NULL;
static void *arg_root_hash_sig = NULL;
static size_t arg_root_hash_sig_size = 0;
static size_t arg_root_hash_size = 0; static size_t arg_root_hash_size = 0;
static char **arg_syscall_allow_list = NULL; static char **arg_syscall_allow_list = NULL;
static char **arg_syscall_deny_list = NULL; static char **arg_syscall_deny_list = NULL;
@ -244,6 +247,8 @@ STATIC_DESTRUCTOR_REGISTER(arg_property_message, sd_bus_message_unrefp);
STATIC_DESTRUCTOR_REGISTER(arg_parameters, strv_freep); STATIC_DESTRUCTOR_REGISTER(arg_parameters, strv_freep);
STATIC_DESTRUCTOR_REGISTER(arg_root_hash, freep); STATIC_DESTRUCTOR_REGISTER(arg_root_hash, freep);
STATIC_DESTRUCTOR_REGISTER(arg_verity_data, freep); STATIC_DESTRUCTOR_REGISTER(arg_verity_data, freep);
STATIC_DESTRUCTOR_REGISTER(arg_root_hash_sig_path, freep);
STATIC_DESTRUCTOR_REGISTER(arg_root_hash_sig, freep);
STATIC_DESTRUCTOR_REGISTER(arg_syscall_allow_list, strv_freep); STATIC_DESTRUCTOR_REGISTER(arg_syscall_allow_list, strv_freep);
STATIC_DESTRUCTOR_REGISTER(arg_syscall_deny_list, strv_freep); STATIC_DESTRUCTOR_REGISTER(arg_syscall_deny_list, strv_freep);
#if HAVE_SECCOMP #if HAVE_SECCOMP
@ -305,6 +310,10 @@ static int help(void) {
" --read-only Mount the root directory read-only\n" " --read-only Mount the root directory read-only\n"
" --volatile[=MODE] Run the system in volatile mode\n" " --volatile[=MODE] Run the system in volatile mode\n"
" --root-hash=HASH Specify verity root hash for root disk image\n" " --root-hash=HASH Specify verity root hash for root disk image\n"
" --root-hash-sig=SIG Specify pkcs7 signature of root hash for verity\n"
" as a DER encoded PKCS7, either as a path to a file\n"
" or as an ASCII base64 encoded string prefixed by\n"
" 'base64:'\n"
" --verity-data=PATH Specify hash device for verity\n" " --verity-data=PATH Specify hash device for verity\n"
" --pivot-root=PATH[:PATH]\n" " --pivot-root=PATH[:PATH]\n"
" Pivot root to given directory in the container\n\n" " Pivot root to given directory in the container\n\n"
@ -667,6 +676,7 @@ static int parse_argv(int argc, char *argv[]) {
ARG_OCI_BUNDLE, ARG_OCI_BUNDLE,
ARG_NO_PAGER, ARG_NO_PAGER,
ARG_VERITY_DATA, ARG_VERITY_DATA,
ARG_ROOT_HASH_SIG,
}; };
static const struct option options[] = { static const struct option options[] = {
@ -733,6 +743,7 @@ static int parse_argv(int argc, char *argv[]) {
{ "oci-bundle", required_argument, NULL, ARG_OCI_BUNDLE }, { "oci-bundle", required_argument, NULL, ARG_OCI_BUNDLE },
{ "no-pager", no_argument, NULL, ARG_NO_PAGER }, { "no-pager", no_argument, NULL, ARG_NO_PAGER },
{ "verity-data", required_argument, NULL, ARG_VERITY_DATA }, { "verity-data", required_argument, NULL, ARG_VERITY_DATA },
{ "root-hash-sig", required_argument, NULL, ARG_ROOT_HASH_SIG },
{} {}
}; };
@ -1327,6 +1338,31 @@ static int parse_argv(int argc, char *argv[]) {
return r; return r;
break; break;
case ARG_ROOT_HASH_SIG: {
char *value;
if ((value = startswith(optarg, "base64:"))) {
void *p;
size_t l;
r = unbase64mem(value, strlen(value), &p, &l);
if (r < 0)
return log_error_errno(r, "Failed to parse root hash signature '%s': %m", optarg);
free_and_replace(arg_root_hash_sig, p);
arg_root_hash_sig_size = l;
arg_root_hash_sig_path = mfree(arg_root_hash_sig_path);
} else {
r = parse_path_argument_and_warn(optarg, false, &arg_root_hash_sig_path);
if (r < 0)
return r;
arg_root_hash_sig = mfree(arg_root_hash_sig);
arg_root_hash_sig_size = 0;
}
break;
}
case ARG_SYSTEM_CALL_FILTER: { case ARG_SYSTEM_CALL_FILTER: {
bool negative; bool negative;
const char *items; const char *items;
@ -5143,7 +5179,8 @@ static int run(int argc, char *argv[]) {
} }
r = verity_metadata_load(arg_image, NULL, arg_root_hash ? NULL : &arg_root_hash, &arg_root_hash_size, r = verity_metadata_load(arg_image, NULL, arg_root_hash ? NULL : &arg_root_hash, &arg_root_hash_size,
arg_verity_data ? NULL : &arg_verity_data); arg_verity_data ? NULL : &arg_verity_data,
arg_root_hash_sig_path || arg_root_hash_sig ? NULL : &arg_root_hash_sig_path);
if (r < 0) { if (r < 0) {
log_error_errno(r, "Failed to read verity artefacts for %s: %m", arg_image); log_error_errno(r, "Failed to read verity artefacts for %s: %m", arg_image);
goto finish; goto finish;
@ -5193,7 +5230,7 @@ static int run(int argc, char *argv[]) {
if (!arg_root_hash && dissected_image->can_verity) if (!arg_root_hash && dissected_image->can_verity)
log_notice("Note: image %s contains verity information, but no root hash specified! Proceeding without integrity checking.", arg_image); log_notice("Note: image %s contains verity information, but no root hash specified! Proceeding without integrity checking.", arg_image);
r = dissected_image_decrypt_interactively(dissected_image, NULL, arg_root_hash, arg_root_hash_size, arg_verity_data, 0, &decrypted_image); r = dissected_image_decrypt_interactively(dissected_image, NULL, arg_root_hash, arg_root_hash_size, arg_verity_data, arg_root_hash_sig_path, arg_root_hash_sig, arg_root_hash_sig_size, 0, &decrypted_image);
if (r < 0) if (r < 0)
goto finish; goto finish;

View File

@ -1223,6 +1223,9 @@ static int verity_partition(
const void *root_hash, const void *root_hash,
size_t root_hash_size, size_t root_hash_size,
const char *verity_data, const char *verity_data,
const char *root_hash_sig_path,
const void *root_hash_sig,
size_t root_hash_sig_size,
DissectImageFlags flags, DissectImageFlags flags,
DecryptedImage *d) { DecryptedImage *d) {
@ -1267,7 +1270,25 @@ static int verity_partition(
if (r < 0) if (r < 0)
return r; return r;
r = crypt_activate_by_volume_key(cd, name, root_hash, root_hash_size, CRYPT_ACTIVATE_READONLY); if (root_hash_sig || root_hash_sig_path) {
#if HAVE_CRYPT_ACTIVATE_BY_SIGNED_KEY
if (root_hash_sig)
r = crypt_activate_by_signed_key(cd, name, root_hash, root_hash_size, root_hash_sig, root_hash_sig_size, CRYPT_ACTIVATE_READONLY);
else {
_cleanup_free_ char *hash_sig = NULL;
size_t hash_sig_size;
r = read_full_file_full(AT_FDCWD, root_hash_sig_path, 0, &hash_sig, &hash_sig_size);
if (r < 0)
return r;
r = crypt_activate_by_signed_key(cd, name, root_hash, root_hash_size, hash_sig, hash_sig_size, CRYPT_ACTIVATE_READONLY);
}
#else
r = log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "activation of verity device with signature requested, but not supported by cryptsetup due to missing crypt_activate_by_signed_key()");
#endif
} else
r = crypt_activate_by_volume_key(cd, name, root_hash, root_hash_size, CRYPT_ACTIVATE_READONLY);
if (r < 0) if (r < 0)
return r; return r;
@ -1287,6 +1308,9 @@ int dissected_image_decrypt(
const void *root_hash, const void *root_hash,
size_t root_hash_size, size_t root_hash_size,
const char *verity_data, const char *verity_data,
const char *root_hash_sig_path,
const void *root_hash_sig,
size_t root_hash_sig_size,
DissectImageFlags flags, DissectImageFlags flags,
DecryptedImage **ret) { DecryptedImage **ret) {
@ -1333,7 +1357,7 @@ int dissected_image_decrypt(
k = PARTITION_VERITY_OF(i); k = PARTITION_VERITY_OF(i);
if (k >= 0) { if (k >= 0) {
r = verity_partition(p, m->partitions + k, root_hash, root_hash_size, verity_data, flags, d); r = verity_partition(p, m->partitions + k, root_hash, root_hash_size, verity_data, root_hash_sig_path, root_hash_sig, root_hash_sig_size, flags, d);
if (r < 0) if (r < 0)
return r; return r;
} }
@ -1359,6 +1383,9 @@ int dissected_image_decrypt_interactively(
const void *root_hash, const void *root_hash,
size_t root_hash_size, size_t root_hash_size,
const char *verity_data, const char *verity_data,
const char *root_hash_sig_path,
const void *root_hash_sig,
size_t root_hash_sig_size,
DissectImageFlags flags, DissectImageFlags flags,
DecryptedImage **ret) { DecryptedImage **ret) {
@ -1369,7 +1396,7 @@ int dissected_image_decrypt_interactively(
n--; n--;
for (;;) { for (;;) {
r = dissected_image_decrypt(m, passphrase, root_hash, root_hash_size, verity_data, flags, ret); r = dissected_image_decrypt(m, passphrase, root_hash, root_hash_size, verity_data, root_hash_sig_path, root_hash_sig, root_hash_sig_size, flags, ret);
if (r >= 0) if (r >= 0)
return r; return r;
if (r == -EKEYREJECTED) if (r == -EKEYREJECTED)
@ -1421,8 +1448,8 @@ int decrypted_image_relinquish(DecryptedImage *d) {
return 0; return 0;
} }
int verity_metadata_load(const char *image, const char *root_hash_path, void **ret_roothash, size_t *ret_roothash_size, char **ret_verity_data) { int verity_metadata_load(const char *image, const char *root_hash_path, void **ret_roothash, size_t *ret_roothash_size, char **ret_verity_data, char **ret_roothashsig) {
_cleanup_free_ char *verity_filename = NULL; _cleanup_free_ char *verity_filename = NULL, *roothashsig_filename = NULL;
_cleanup_free_ void *roothash_decoded = NULL; _cleanup_free_ void *roothash_decoded = NULL;
size_t roothash_decoded_size = 0; size_t roothash_decoded_size = 0;
int r; int r;
@ -1437,6 +1464,8 @@ int verity_metadata_load(const char *image, const char *root_hash_path, void **r
*ret_roothash_size = 0; *ret_roothash_size = 0;
if (ret_verity_data) if (ret_verity_data)
*ret_verity_data = NULL; *ret_verity_data = NULL;
if (ret_roothashsig)
*ret_roothashsig = NULL;
return 0; return 0;
} }
@ -1461,6 +1490,29 @@ int verity_metadata_load(const char *image, const char *root_hash_path, void **r
} }
} }
if (ret_roothashsig) {
char *e;
/* Follow naming convention recommended by the relevant RFC:
* https://tools.ietf.org/html/rfc5751#section-3.2.1 */
roothashsig_filename = new(char, strlen(image) + STRLEN(".roothash.p7s") + 1);
if (!roothashsig_filename)
return -ENOMEM;
strcpy(roothashsig_filename, image);
e = endswith(roothashsig_filename, ".raw");
if (e)
strcpy(e, ".roothash.p7s");
else
strcat(roothashsig_filename, ".roothash.p7s");
r = access(roothashsig_filename, R_OK);
if (r < 0) {
if (errno != ENOENT)
return -errno;
roothashsig_filename = mfree(roothashsig_filename);
}
}
if (ret_roothash) { if (ret_roothash) {
_cleanup_free_ char *text = NULL; _cleanup_free_ char *text = NULL;
assert(ret_roothash_size); assert(ret_roothash_size);
@ -1507,6 +1559,8 @@ int verity_metadata_load(const char *image, const char *root_hash_path, void **r
} }
if (ret_verity_data) if (ret_verity_data)
*ret_verity_data = TAKE_PTR(verity_filename); *ret_verity_data = TAKE_PTR(verity_filename);
if (roothashsig_filename)
*ret_roothashsig = TAKE_PTR(roothashsig_filename);
return 1; return 1;
} }

View File

@ -87,8 +87,8 @@ int dissect_image_and_warn(int fd, const char *name, const void *root_hash, size
DissectedImage* dissected_image_unref(DissectedImage *m); DissectedImage* dissected_image_unref(DissectedImage *m);
DEFINE_TRIVIAL_CLEANUP_FUNC(DissectedImage*, dissected_image_unref); DEFINE_TRIVIAL_CLEANUP_FUNC(DissectedImage*, dissected_image_unref);
int dissected_image_decrypt(DissectedImage *m, const char *passphrase, const void *root_hash, size_t root_hash_size, const char *verity_data, DissectImageFlags flags, DecryptedImage **ret); int dissected_image_decrypt(DissectedImage *m, const char *passphrase, const void *root_hash, size_t root_hash_size, const char *verity_data, const char *root_hash_sig_path, const void *root_hash_sig, size_t root_hash_sig_size, DissectImageFlags flags, DecryptedImage **ret);
int dissected_image_decrypt_interactively(DissectedImage *m, const char *passphrase, const void *root_hash, size_t root_hash_size, const char *verity_data, DissectImageFlags flags, DecryptedImage **ret); int dissected_image_decrypt_interactively(DissectedImage *m, const char *passphrase, const void *root_hash, size_t root_hash_size, const char *verity_data, const char *root_hash_sig_path, const void *root_hash_sig, size_t root_hash_sig_size, DissectImageFlags flags, DecryptedImage **ret);
int dissected_image_mount(DissectedImage *m, const char *dest, uid_t uid_shift, DissectImageFlags flags); int dissected_image_mount(DissectedImage *m, const char *dest, uid_t uid_shift, DissectImageFlags flags);
int dissected_image_acquire_metadata(DissectedImage *m); int dissected_image_acquire_metadata(DissectedImage *m);
@ -100,6 +100,6 @@ int decrypted_image_relinquish(DecryptedImage *d);
const char* partition_designator_to_string(int i) _const_; const char* partition_designator_to_string(int i) _const_;
int partition_designator_from_string(const char *name) _pure_; int partition_designator_from_string(const char *name) _pure_;
int verity_metadata_load(const char *image, const char *root_hash_path, void **ret_roothash, size_t *ret_roothash_size, char **ret_verity_data); int verity_metadata_load(const char *image, const char *root_hash_path, void **ret_roothash, size_t *ret_roothash_size, char **ret_verity_data, char **ret_roothashsig);
bool dissected_image_can_do_verity(const DissectedImage *image, unsigned partition_designator); bool dissected_image_can_do_verity(const DissectedImage *image, unsigned partition_designator);
bool dissected_image_has_verity(const DissectedImage *image, unsigned partition_designator); bool dissected_image_has_verity(const DissectedImage *image, unsigned partition_designator);