lib/deltas: Add inline signature for static-delta superblock
authorFrédéric Danis <frederic.danis@collabora.com>
Wed, 8 Jul 2020 10:15:15 +0000 (12:15 +0200)
committerFrédéric Danis <frederic.danis@collabora.com>
Mon, 14 Sep 2020 07:27:19 +0000 (09:27 +0200)
While the commits contained in the single static-delta file are signed so
we can check them and operate on trusted data, the superblock isn't signed
in any way, so it end up operating on untrusted data to:
 1. actually find where the trusted data is, and
 2. check whether the update is fit for the current device by looking at
    the collection id stored in the metadata

This commit generates signatures of all static data, and concatenate them
to the existing static delta format, i.e. as a GVariant layout `a{sv}ay`
where
 - a{sv}: signatures
 - ay: existing delta variant

Signed-off-by: Frédéric Danis <frederic.danis@collabora.com>
src/libostree/ostree-repo-static-delta-compilation.c
src/libostree/ostree-repo-static-delta-private.h

index 3e9a8e30d006a0be888a187f5ecd153ffe7e8731..753f8aee0066bf968ae07ba91813195ea58ad89d 100644 (file)
@@ -36,6 +36,8 @@
 #include "libglnx.h"
 #include "ostree-varint.h"
 #include "bsdiff/bsdiff.h"
+#include "ostree-autocleanups.h"
+#include "ostree-sign.h"
 
 #define CONTENT_SIZE_SIMILARITY_THRESHOLD_PERCENT (30)
 
@@ -1335,6 +1337,8 @@ get_fallback_headers (OstreeRepo               *self,
  *   - verbose: b: Print diagnostic messages.  Default FALSE.
  *   - endianness: b: Deltas use host byte order by default; this option allows choosing (G_BIG_ENDIAN or G_LITTLE_ENDIAN)
  *   - filename: ay: Save delta superblock to this filename, and parts in the same directory.  Default saves to repository.
+ *   - sign-name: ay: Signature type to use.
+ *   - sign-key-ids: as: Array of keys used to sign delta superblock.
  */
 gboolean
 ostree_repo_static_delta_generate (OstreeRepo                   *self,
@@ -1368,6 +1372,8 @@ ostree_repo_static_delta_generate (OstreeRepo                   *self,
   g_autoptr(GPtrArray) builder_fallback_objects = g_ptr_array_new_with_free_func ((GDestroyNotify)g_variant_unref);
   g_auto(GLnxTmpfile) descriptor_tmpf = { 0, };
   g_autoptr(OtVariantBuilder) descriptor_builder = NULL;
+  const char *opt_sign_name;
+  const char **opt_key_ids;
 
   if (!g_variant_lookup (params, "min-fallback-size", "u", &min_fallback_size))
     min_fallback_size = 4;
@@ -1407,6 +1413,12 @@ ostree_repo_static_delta_generate (OstreeRepo                   *self,
   if (!g_variant_lookup (params, "filename", "^&ay", &opt_filename))
     opt_filename = NULL;
 
+  if (!g_variant_lookup (params, "sign-name", "^&ay", &opt_sign_name))
+    opt_sign_name = NULL;
+
+  if (!g_variant_lookup (params, "sign-key-ids", "^a&s", &opt_key_ids))
+    opt_key_ids = NULL;
+
   if (!ostree_repo_load_variant (self, OSTREE_OBJECT_TYPE_COMMIT, to,
                                  &to_commit, error))
     return FALSE;
@@ -1442,7 +1454,7 @@ ostree_repo_static_delta_generate (OstreeRepo                   *self,
                                   cancellable, error))
     return FALSE;
 
-  if (!glnx_open_tmpfile_linkable_at (descriptor_dfd, ".", O_WRONLY | O_CLOEXEC,
+  if (!glnx_open_tmpfile_linkable_at (descriptor_dfd, ".", O_RDWR | O_CLOEXEC,
                                       &descriptor_tmpf, error))
     return FALSE;
 
@@ -1586,12 +1598,85 @@ ostree_repo_static_delta_generate (OstreeRepo                   *self,
       g_printerr ("bsdiff=%u objects\n", builder.n_bsdiff);
     }
 
-  if (fchmod (descriptor_tmpf.fd, 0644) < 0)
-    return glnx_throw_errno_prefix (error, "fchmod");
+  if (opt_sign_name != NULL && opt_key_ids != NULL)
+    {
+      g_autoptr(GBytes) tmpdata = NULL;
+      g_autoptr(OstreeSign) sign = NULL;
+      const gchar *signature_key = NULL;
+      g_autoptr(GVariantBuilder) signature_builder = NULL;
+      g_auto(GLnxTmpfile) descriptor_sign_tmpf = { 0, };
+      g_autoptr(OtVariantBuilder) descriptor_sign_builder = NULL;
+
+      lseek (descriptor_tmpf.fd, 0, SEEK_SET);
+      tmpdata = glnx_fd_readall_bytes (descriptor_tmpf.fd, cancellable, error);
+      if (!tmpdata)
+        return FALSE;
 
-  if (!glnx_link_tmpfile_at (&descriptor_tmpf, GLNX_LINK_TMPFILE_REPLACE,
-                             descriptor_dfd, descriptor_name, error))
-    return FALSE;
+      sign = ostree_sign_get_by_name (opt_sign_name, error);
+      if (sign == NULL)
+        return FALSE;
+
+      signature_key = ostree_sign_metadata_key (sign);
+      const gchar *signature_format = ostree_sign_metadata_format (sign);
+
+      signature_builder = g_variant_builder_new (G_VARIANT_TYPE (signature_format));
+
+      for (const char **iter = opt_key_ids; iter && *iter; iter++)
+        {
+          const char *keyid = *iter;
+          g_autoptr(GVariant) secret_key = NULL;
+          g_autoptr(GBytes) signature_bytes = NULL;
+
+          secret_key = g_variant_new_string (keyid);
+          if (!ostree_sign_set_sk (sign, secret_key, error))
+              return FALSE;
+
+          if (!ostree_sign_data (sign, tmpdata, &signature_bytes,
+                                 NULL, error))
+            return FALSE;
+
+          g_variant_builder_add (signature_builder, "@ay", ot_gvariant_new_ay_bytes (signature_bytes));
+        }
+
+      if (!glnx_open_tmpfile_linkable_at (descriptor_dfd, ".", O_WRONLY | O_CLOEXEC,
+                                          &descriptor_sign_tmpf, error))
+        return FALSE;
+
+      descriptor_sign_builder = ot_variant_builder_new (G_VARIANT_TYPE (OSTREE_STATIC_DELTA_SIGNED_FORMAT),
+                                                        descriptor_sign_tmpf.fd);
+
+      if (!ot_variant_builder_add (descriptor_sign_builder, error, "t",
+                                   GUINT64_TO_BE (OSTREE_STATIC_DELTA_SIGNED_MAGIC)))
+        return FALSE;
+      if (!ot_variant_builder_add (descriptor_sign_builder, error, "@ay", ot_gvariant_new_ay_bytes (tmpdata)))
+        return FALSE;
+      if (!ot_variant_builder_open (descriptor_sign_builder, G_VARIANT_TYPE ("a{sv}"), error))
+        return FALSE;
+      if (!ot_variant_builder_add (descriptor_sign_builder, error, "{sv}",
+                                   signature_key, g_variant_builder_end(signature_builder)))
+        return FALSE;
+      if (!ot_variant_builder_close (descriptor_sign_builder, error))
+        return FALSE;
+
+      if (!ot_variant_builder_end (descriptor_sign_builder, error))
+        return FALSE;
+
+      if (fchmod (descriptor_sign_tmpf.fd, 0644) < 0)
+        return glnx_throw_errno_prefix (error, "fchmod");
+
+      if (!glnx_link_tmpfile_at (&descriptor_sign_tmpf, GLNX_LINK_TMPFILE_REPLACE,
+                                 descriptor_dfd, descriptor_name, error))
+        return FALSE;
+    }
+  else
+    {
+      if (fchmod (descriptor_tmpf.fd, 0644) < 0)
+        return glnx_throw_errno_prefix (error, "fchmod");
+
+      if (!glnx_link_tmpfile_at (&descriptor_tmpf, GLNX_LINK_TMPFILE_REPLACE,
+                                 descriptor_dfd, descriptor_name, error))
+        return FALSE;
+    }
 
   return TRUE;
 }
index ff8de9d4fa37b0801d4f2d59ef2ba3f8d2c8befa..5a2e687922da90466dbc5404e270f1867e9b86d6 100644 (file)
@@ -104,6 +104,25 @@ G_BEGIN_DECLS
  */ 
 #define OSTREE_STATIC_DELTA_SUPERBLOCK_FORMAT "(a{sv}tayay" OSTREE_COMMIT_GVARIANT_STRING "aya" OSTREE_STATIC_DELTA_META_ENTRY_FORMAT "a" OSTREE_STATIC_DELTA_FALLBACK_FORMAT ")"
 
+/**
+ * OSTREE_STATIC_DELTA_SIGNED_FORMAT
+ *
+ *   magic: t magic number, 8 bytes for alignment
+ *   superblock: ay delta supeblock variant
+ *   signatures: a{sv}
+ *
+ * The signed static delta starts with the 'OSTSGNDT' magic number followed by
+ * the array of bytes containing the superblock used for the signature.
+ *
+ * Then, the signatures array contains the signatures of the superblock. A
+ * signature has the following form:
+ *  type: signature key
+ *  signature: variant depending on type used
+ */
+#define OSTREE_STATIC_DELTA_SIGNED_FORMAT "(taya{sv})"
+
+#define OSTREE_STATIC_DELTA_SIGNED_MAGIC  0x4F535453474E4454 /* OSTSGNDT */
+
 typedef enum {
   OSTREE_STATIC_DELTA_OPEN_FLAGS_NONE = 0,
   OSTREE_STATIC_DELTA_OPEN_FLAGS_SKIP_CHECKSUM = (1 << 0),