lib/deltas: Add inline signature for static-delta superblock

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>
This commit is contained in:
Frédéric Danis 2020-07-08 12:15:15 +02:00
parent 9c040c1a73
commit 46667567c5
2 changed files with 110 additions and 6 deletions

View 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;
if (!glnx_link_tmpfile_at (&descriptor_tmpf, GLNX_LINK_TMPFILE_REPLACE,
descriptor_dfd, descriptor_name, error))
return FALSE;
lseek (descriptor_tmpf.fd, 0, SEEK_SET);
tmpdata = glnx_fd_readall_bytes (descriptor_tmpf.fd, cancellable, error);
if (!tmpdata)
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;
}

View 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),