From fb1faf17d6f9cefd349c46f48f7a28f269f07576 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Danis?= Date: Tue, 25 Aug 2020 09:26:09 +0200 Subject: [PATCH] lib/deltas: Check signed delta in execute_offline MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add a new function `ostree_repo_static_delta_execute_offline_with_signature` which takes a signature engine to verify the delta before applying it. The `ostree_repo_static_delta_execute_offline` is just a wrapper to this new function, passing a NULL signature engine. When this function is called without signature engine, but with a sign delta, it will only fails if `sign-verify-deltas` is set to true in repo core options. This commits move signature existence check and delta signature verification to share common parts between existing APIs and the new function. Signed-off-by: Frédéric Danis --- apidoc/ostree-sections.txt | 1 + src/libostree/libostree-devel.sym | 1 + src/libostree/ostree-repo-static-delta-core.c | 132 +++++++++++++----- src/libostree/ostree-repo.h | 8 ++ 4 files changed, 108 insertions(+), 34 deletions(-) diff --git a/apidoc/ostree-sections.txt b/apidoc/ostree-sections.txt index 8cafeb1f..66b42233 100644 --- a/apidoc/ostree-sections.txt +++ b/apidoc/ostree-sections.txt @@ -412,6 +412,7 @@ ostree_repo_list_commit_objects_starting_with ostree_repo_list_static_delta_names OstreeStaticDeltaGenerateOpt ostree_repo_static_delta_generate +ostree_repo_static_delta_execute_offline_with_signature ostree_repo_static_delta_execute_offline ostree_repo_static_delta_verify_signature ostree_repo_traverse_new_reachable diff --git a/src/libostree/libostree-devel.sym b/src/libostree/libostree-devel.sym index 9a847b92..a15654c1 100644 --- a/src/libostree/libostree-devel.sym +++ b/src/libostree/libostree-devel.sym @@ -22,6 +22,7 @@ global: /* Add symbols here, and uncomment the bits in * Makefile-libostree.am to enable this too. */ + ostree_repo_static_delta_execute_offline_with_signature; ostree_repo_static_delta_verify_signature; } LIBOSTREE_2020.4; diff --git a/src/libostree/ostree-repo-static-delta-core.c b/src/libostree/ostree-repo-static-delta-core.c index 8d0f4e82..b16764ae 100644 --- a/src/libostree/ostree-repo-static-delta-core.c +++ b/src/libostree/ostree-repo-static-delta-core.c @@ -265,25 +265,68 @@ _ostree_repo_static_delta_is_signed (OstreeRepo *self, return ret; } +static gboolean +_ostree_repo_static_delta_verify_signature (OstreeRepo *self, + int fd, + OstreeSign *sign, + char **out_success_message, + GError **error) +{ + g_autoptr(GVariantBuilder) desc_sign_builder = NULL; + g_autoptr(GVariant) delta_meta = NULL; + g_autoptr(GVariant) delta = NULL; + + if (!ot_variant_read_fd (fd, 0, + (GVariantType*)OSTREE_STATIC_DELTA_SIGNED_FORMAT, + TRUE, &delta, error)) + return FALSE; + + /* Check if there are signatures for signature engine */ + const gchar *signature_key = ostree_sign_metadata_key(sign); + GVariantType *signature_format = (GVariantType *) ostree_sign_metadata_format(sign); + delta_meta = g_variant_get_child_value (delta, 2); + if (delta_meta == NULL) + return glnx_throw (error, "no metadata in static-delta superblock"); + g_autoptr(GVariant) signatures = g_variant_lookup_value (delta_meta, + signature_key, + signature_format); + if (!signatures) + return glnx_throw (error, "no signature for '%s' in static-delta superblock", signature_key); + + /* Get static delta superblock */ + g_autoptr(GVariant) child = g_variant_get_child_value (delta, 1); + if (child == NULL) + return glnx_throw (error, "no metadata in static-delta superblock"); + g_autoptr(GBytes) signed_data = g_variant_get_data_as_bytes(child); + + return ostree_sign_data_verify (sign, signed_data, signatures, out_success_message, error); +} + /** - * ostree_repo_static_delta_execute_offline: + * ostree_repo_static_delta_execute_offline_with_signature: * @self: Repo * @dir_or_file: Path to a directory containing static delta data, or directly to the superblock + * @sign: Signature engine used to check superblock * @skip_validation: If %TRUE, assume data integrity * @cancellable: Cancellable * @error: Error * * Given a directory representing an already-downloaded static delta - * on disk, apply it, generating a new commit. The directory must be - * named with the form "FROM-TO", where both are checksums, and it - * must contain a file named "superblock", along with at least one part. + * on disk, apply it, generating a new commit. + * If sign is passed, the static delta signature is verified. + * If sign-verify-deltas configuration option is set and static delta is signed, + * signature verification will be mandatory before apply the static delta. + * The directory must be named with the form "FROM-TO", where both are + * checksums, and it must contain a file named "superblock", along with at least + * one part. */ gboolean -ostree_repo_static_delta_execute_offline (OstreeRepo *self, - GFile *dir_or_file, - gboolean skip_validation, - GCancellable *cancellable, - GError **error) +ostree_repo_static_delta_execute_offline_with_signature (OstreeRepo *self, + GFile *dir_or_file, + OstreeSign *sign, + gboolean skip_validation, + GCancellable *cancellable, + GError **error) { g_autofree char *basename = NULL; g_autoptr(GVariant) delta = NULL; @@ -316,6 +359,25 @@ ostree_repo_static_delta_execute_offline (OstreeRepo *self, gboolean is_signed = _ostree_repo_static_delta_is_signed (self, meta_fd, NULL, NULL); if (is_signed) { + gboolean verify_deltas; + gboolean verified; + + if (!ot_keyfile_get_boolean_with_default (self->config, "core", "sign-verify-deltas", + FALSE, &verify_deltas, error)) + return FALSE; + + if (verify_deltas && !sign) + return glnx_throw (error, "Key is mandatory to check delta signature"); + + if (sign) + { + verified = _ostree_repo_static_delta_verify_signature (self, meta_fd, sign, NULL, error); + if (*error) + return FALSE; + if (!verified) + return glnx_throw (error, "Delta signature verification failed"); + } + if (!ot_variant_read_fd (meta_fd, 0, (GVariantType*)OSTREE_STATIC_DELTA_SIGNED_FORMAT, TRUE, &delta, error)) return FALSE; @@ -479,6 +541,32 @@ ostree_repo_static_delta_execute_offline (OstreeRepo *self, return TRUE; } +/** + * ostree_repo_static_delta_execute_offline: + * @self: Repo + * @dir_or_file: Path to a directory containing static delta data, or directly to the superblock + * @skip_validation: If %TRUE, assume data integrity + * @cancellable: Cancellable + * @error: Error + * + * Given a directory representing an already-downloaded static delta + * on disk, apply it, generating a new commit. The directory must be + * named with the form "FROM-TO", where both are checksums, and it + * must contain a file named "superblock", along with at least one part. + */ +gboolean +ostree_repo_static_delta_execute_offline (OstreeRepo *self, + GFile *dir_or_file, + gboolean skip_validation, + GCancellable *cancellable, + GError **error) +{ + return ostree_repo_static_delta_execute_offline_with_signature(self, dir_or_file, NULL, + skip_validation, + cancellable, + error); +} + gboolean _ostree_static_delta_part_open (GInputStream *part_in, GBytes *inline_part_bytes, @@ -1030,29 +1118,5 @@ ostree_repo_static_delta_verify_signature (OstreeRepo *self, if (!_ostree_repo_static_delta_is_signed (self, delta_fd, NULL, error)) return FALSE; - g_autoptr(GVariant) delta = NULL; - if (!ot_variant_read_fd (delta_fd, 0, - (GVariantType*)OSTREE_STATIC_DELTA_SIGNED_FORMAT, - TRUE, &delta, error)) - return FALSE; - - /* Check if there are signatures for signature engine */ - const gchar *signature_key = ostree_sign_metadata_key(sign); - GVariantType *signature_format = (GVariantType *) ostree_sign_metadata_format(sign); - delta_meta = g_variant_get_child_value (delta, 2); - if (delta_meta == NULL) - return glnx_throw (error, "no metadata in static-delta superblock"); - g_autoptr(GVariant) signatures = g_variant_lookup_value (delta_meta, - signature_key, - signature_format); - if (!signatures) - return glnx_throw (error, "no signature for '%s' in static-delta superblock", signature_key); - - /* Get static delta superblock */ - g_autoptr(GVariant) child = g_variant_get_child_value (delta, 1); - if (child == NULL) - return glnx_throw (error, "no metadata in static-delta superblock"); - g_autoptr(GBytes) signed_data = g_variant_get_data_as_bytes(child); - - return ostree_sign_data_verify (sign, signed_data, signatures, out_success_message, error); + return _ostree_repo_static_delta_verify_signature (self, delta_fd, sign, out_success_message, error); } diff --git a/src/libostree/ostree-repo.h b/src/libostree/ostree-repo.h index 552c1e61..d52fc9c3 100644 --- a/src/libostree/ostree-repo.h +++ b/src/libostree/ostree-repo.h @@ -1068,6 +1068,14 @@ gboolean ostree_repo_static_delta_generate (OstreeRepo *self, GCancellable *cancellable, GError **error); +_OSTREE_PUBLIC +gboolean ostree_repo_static_delta_execute_offline_with_signature (OstreeRepo *self, + GFile *dir_or_file, + OstreeSign *sign, + gboolean skip_validation, + GCancellable *cancellable, + GError **error); + _OSTREE_PUBLIC gboolean ostree_repo_static_delta_execute_offline (OstreeRepo *self, GFile *dir_or_file,