lib/deltas: Add signature check API for static-delta superblock
authorFrédéric Danis <frederic.danis@collabora.com>
Thu, 28 Nov 2019 11:18:59 +0000 (12:18 +0100)
committerFrédéric Danis <frederic.danis@collabora.com>
Mon, 14 Sep 2020 07:27:19 +0000 (09:27 +0200)
This retrieves the signatures and pass the static delta block as an array
of bytes to ostree_sign_data_verify().

Signed-off-by: Frédéric Danis <frederic.danis@collabora.com>
Makefile-libostree.am
apidoc/ostree-sections.txt
src/libostree/libostree-devel.sym
src/libostree/ostree-repo-static-delta-core.c
src/libostree/ostree-repo.h

index a180e86b22cb4c48687fb1d840008e4bf9c5a13e..1d31c4d8c96a34864842171613ad7334186aa623 100644 (file)
@@ -183,7 +183,7 @@ endif # USE_GPGME
 
 symbol_files = $(top_srcdir)/src/libostree/libostree-released.sym
 #if BUILDOPT_IS_DEVEL_BUILD
-#symbol_files += $(top_srcdir)/src/libostree/libostree-devel.sym
+symbol_files += $(top_srcdir)/src/libostree/libostree-devel.sym
 #endif
 # http://blog.jgc.org/2007/06/escaping-comma-and-space-in-gnu-make.html
 wl_versionscript_arg = -Wl,--version-script=
index 979c8e9307acf7618b3315b1015bbc81eedeb51e..8cafeb1f82e4d6751d731061b6c5e8933c29d8b8 100644 (file)
@@ -413,6 +413,7 @@ ostree_repo_list_static_delta_names
 OstreeStaticDeltaGenerateOpt
 ostree_repo_static_delta_generate
 ostree_repo_static_delta_execute_offline
+ostree_repo_static_delta_verify_signature
 ostree_repo_traverse_new_reachable
 ostree_repo_traverse_new_parents
 ostree_repo_traverse_parents_get_commits
index 7f1f7e3e6771cd2f000c0b657d368f382972772b..9a847b924a56a54f074671d9625192b135e5d719 100644 (file)
@@ -22,6 +22,7 @@ global:
   /* Add symbols here, and uncomment the bits in
    * Makefile-libostree.am to enable this too.
    */
+  ostree_repo_static_delta_verify_signature;
 } LIBOSTREE_2020.4;
 
 /* Stub section for the stable release *after* this development one; don't
index 835ec7f3e5dded062530cc3d3ccf891694bc22bd..857473db4d14f7080a6f2abd8c7eb23dc79195eb 100644 (file)
@@ -210,6 +210,61 @@ _ostree_repo_static_delta_part_have_all_objects (OstreeRepo             *repo,
   return TRUE;
 }
 
+static gboolean
+_ostree_repo_static_delta_is_signed (OstreeRepo       *self,
+                                     int               fd,
+                                     GPtrArray       **out_value,
+                                     GError          **error)
+{
+  g_autoptr(GVariant) delta = NULL;
+  g_autoptr(GVariant) delta_sign_magic = NULL;
+  g_autoptr(GVariant) delta_sign = NULL;
+  GVariantIter iter;
+  GVariant *item;
+  g_autoptr(GPtrArray) signatures = NULL;
+  gboolean ret = FALSE;
+
+  if (out_value)
+    *out_value = NULL;
+
+  if (!ot_variant_read_fd (fd, 0, (GVariantType*)OSTREE_STATIC_DELTA_SIGNED_FORMAT, TRUE, &delta, error))
+    return FALSE;
+
+  delta_sign_magic = g_variant_get_child_value (delta, 0);
+  if (delta_sign_magic == NULL)
+    return glnx_throw (error, "no signatures in static-delta");
+
+  if (GUINT64_FROM_BE (g_variant_get_uint64 (delta_sign_magic)) != OSTREE_STATIC_DELTA_SIGNED_MAGIC)
+    return glnx_throw (error, "no signatures in static-delta");
+
+  delta_sign = g_variant_get_child_value (delta, 2);
+  if (delta_sign == NULL)
+    return glnx_throw (error, "no signatures in static-delta");
+
+  if (out_value)
+    signatures = g_ptr_array_new_with_free_func (g_free);
+
+  /* Check if there are signatures in the superblock */
+  g_variant_iter_init (&iter, delta_sign);
+  while ((item = g_variant_iter_next_value (&iter)))
+    {
+      g_autoptr(GVariant) key_v = g_variant_get_child_value (item, 0);
+      const char *str = g_variant_get_string (key_v, NULL);
+      if (g_str_has_prefix (str, "ostree.sign."))
+        {
+          ret = TRUE;
+          if (signatures)
+            g_ptr_array_add (signatures, g_strdup (str + strlen ("ostree.sign.")));
+        }
+      g_variant_unref (item);
+    }
+
+  if (out_value && ret)
+    ot_transfer_out_value (out_value, &signatures);
+
+  return ret;
+}
+
 /**
  * ostree_repo_static_delta_execute_offline:
  * @self: Repo
@@ -895,3 +950,76 @@ _ostree_repo_static_delta_dump (OstreeRepo                    *self,
 
   return TRUE;
 }
+
+/**
+ * ostree_repo_static_delta_verify_signature:
+ * @self: Repo
+ * @delta_id: delta path
+ * @sign: Signature engine used to check superblock
+ * @out_success_message: success message
+ * @error: Error
+ *
+ * Verify static delta file signature.
+ *
+ * Returns: TRUE if the signature of static delta file is valid using the
+ * signature engine provided, FALSE otherwise.
+ *
+ * Since: 2020.1
+ */
+gboolean
+ostree_repo_static_delta_verify_signature (OstreeRepo       *self,
+                                           const char       *delta_id,
+                                           OstreeSign       *sign,
+                                           char            **out_success_message,
+                                           GError          **error)
+{
+  g_autoptr(GVariantBuilder) desc_sign_builder = NULL;
+  g_autoptr(GVariant) delta_meta = NULL;
+  glnx_autofd int delta_fd = -1;
+
+  if (strchr (delta_id, '/'))
+    {
+      if (!glnx_openat_rdonly (AT_FDCWD, delta_id, TRUE, &delta_fd, error))
+        return FALSE;
+    }
+  else
+    {
+      g_autofree char *from = NULL;
+      g_autofree char *to = NULL;
+      if (!_ostree_parse_delta_name (delta_id, &from, &to, error))
+        return FALSE;
+
+      g_autofree char *delta_path = _ostree_get_relative_static_delta_superblock_path (from, to);
+      if (!glnx_openat_rdonly (self->repo_dir_fd, delta_path, TRUE, &delta_fd, error))
+        return FALSE;
+    }
+
+  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);
+}
index e28af29cdfc809486577ed77a3df0a1207270eb2..552c1e6188cfaf4dedfc2832e24d1f0e756e3393 100644 (file)
@@ -32,6 +32,7 @@
 #include "ostree-repo-finder.h"
 #include "ostree-sepolicy.h"
 #include "ostree-gpg-verify-result.h"
+#include "ostree-sign.h"
 
 G_BEGIN_DECLS
 
@@ -1074,6 +1075,13 @@ gboolean ostree_repo_static_delta_execute_offline (OstreeRepo
                                                    GCancellable                  *cancellable,
                                                    GError                      **error);
 
+_OSTREE_PUBLIC
+gboolean ostree_repo_static_delta_verify_signature (OstreeRepo       *self,
+                                                    const char       *delta_id,
+                                                    OstreeSign       *sign,
+                                                    char            **out_success_message,
+                                                    GError          **error);
+
 _OSTREE_PUBLIC
 GHashTable *ostree_repo_traverse_new_reachable (void);