Merge pull request #1985 from fdanis-oss/wip/fda/sign_delta_metadata

Static-delta's superblock signature support
This commit is contained in:
OpenShift Merge Robot authored and GitHub committed 2020-09-24 14:21:16 +00:00
commit 00df896550
14 files changed
+1270 -27

No files matched your search

+2
View File
@@ -22,6 +22,8 @@ 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;
/* Stub section for the stable release *after* this development one; don't
@@ -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;
}
+243 -18
View File
@@ -210,27 +210,127 @@ _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;
}
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;
g_autoptr(GVariant) meta = NULL;
const char *dir_or_file_path = gs_file_get_path_cached (dir_or_file);
@@ -256,10 +356,43 @@ ostree_repo_static_delta_execute_offline (OstreeRepo *self,
if (meta_fd < 0)
return glnx_throw_errno_prefix (error, "openat(%s)", basename);
g_autoptr(GVariant) meta = NULL;
if (!ot_variant_read_fd (meta_fd, 0, G_VARIANT_TYPE (OSTREE_STATIC_DELTA_SUPERBLOCK_FORMAT),
FALSE, &meta, error))
return FALSE;
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;
g_autoptr(GVariant) child = g_variant_get_child_value (delta, 1);
g_autoptr(GBytes) bytes = g_variant_get_data_as_bytes (child);
meta = g_variant_new_from_bytes ((GVariantType*)OSTREE_STATIC_DELTA_SUPERBLOCK_FORMAT,
bytes, FALSE);
}
else
{
if (!ot_variant_read_fd (meta_fd, 0, G_VARIANT_TYPE (OSTREE_STATIC_DELTA_SUPERBLOCK_FORMAT),
FALSE, &meta, error))
return FALSE;
}
/* Parsing OSTREE_STATIC_DELTA_SUPERBLOCK_FORMAT */
@@ -408,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,
@@ -748,6 +907,8 @@ _ostree_repo_static_delta_dump (OstreeRepo *self,
GError **error)
{
glnx_autofd int superblock_fd = -1;
g_autoptr(GVariant) delta = NULL;
g_autoptr(GVariant) delta_superblock = NULL;
if (strchr (delta_id, '/'))
{
@@ -766,13 +927,28 @@ _ostree_repo_static_delta_dump (OstreeRepo *self,
return FALSE;
}
g_autoptr(GVariant) delta_superblock = NULL;
if (!ot_variant_read_fd (superblock_fd, 0,
(GVariantType*)OSTREE_STATIC_DELTA_SUPERBLOCK_FORMAT,
TRUE, &delta_superblock, error))
return FALSE;
gboolean is_signed = _ostree_repo_static_delta_is_signed(self, superblock_fd, NULL, NULL);
if (is_signed)
{
if (!ot_variant_read_fd (superblock_fd, 0, (GVariantType*)OSTREE_STATIC_DELTA_SIGNED_FORMAT,
TRUE, &delta, error))
return FALSE;
g_autoptr(GVariant) child = g_variant_get_child_value (delta, 1);
g_autoptr(GBytes) bytes = g_variant_get_data_as_bytes(child);
delta_superblock = g_variant_new_from_bytes ((GVariantType*)OSTREE_STATIC_DELTA_SUPERBLOCK_FORMAT,
bytes, FALSE);
}
else
{
if (!ot_variant_read_fd (superblock_fd, 0,
(GVariantType*)OSTREE_STATIC_DELTA_SUPERBLOCK_FORMAT,
TRUE, &delta_superblock, error))
return FALSE;
}
g_print ("Delta: %s\n", delta_id);
g_print ("Signed: %s\n", is_signed ? "yes" : "no");
g_autoptr(GVariant) from_commit_v = NULL;
g_variant_get_child (delta_superblock, 2, "@ay", &from_commit_v);
g_autofree char *from_commit = NULL;
@@ -895,3 +1071,52 @@ _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;
return _ostree_repo_static_delta_verify_signature (self, delta_fd, sign, out_success_message, error);
}
@@ -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),
+16
View 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
@@ -1067,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,
@@ -1074,6 +1083,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);