mirror of
https://github.com/flatpak/flatpak.git
synced 2026-05-19 06:11:47 -04:00
common: Check signatures when installing OCI images
Co-authored-by: Sebastian Wick <sebastian.wick@redhat.com>
This commit is contained in:
committed by
Sebastian Wick
parent
841f33e451
commit
404aa33ce1
@@ -68,7 +68,7 @@ import_oci (OstreeRepo *repo, GFile *file,
|
||||
|
||||
ref = flatpak_image_source_get_ref (image_source);
|
||||
|
||||
commit_checksum = flatpak_pull_from_oci (repo, image_source, NULL,
|
||||
commit_checksum = flatpak_pull_from_oci (repo, image_source, NULL, NULL,
|
||||
ref, FLATPAK_PULL_FLAGS_NONE,
|
||||
NULL, NULL, cancellable, error);
|
||||
if (commit_checksum == NULL)
|
||||
|
||||
@@ -172,6 +172,13 @@ void flatpak_remote_state_add_sideload_dir (FlatpakRemoteState *self,
|
||||
GFile *path);
|
||||
void flatpak_remote_state_add_sideload_image_collection (FlatpakRemoteState *self,
|
||||
FlatpakImageCollection *image_collection);
|
||||
FlatpakImageSource * flatpak_remote_state_fetch_image_source (FlatpakRemoteState *self,
|
||||
FlatpakDir *dir,
|
||||
const char *ref,
|
||||
const char *opt_rev,
|
||||
const char *token,
|
||||
GCancellable *cancellable,
|
||||
GError **error);
|
||||
|
||||
|
||||
G_DEFINE_AUTOPTR_CLEANUP_FUNC (FlatpakDir, g_object_unref)
|
||||
|
||||
@@ -185,6 +185,9 @@ static gboolean flatpak_dir_lookup_remote_filter (FlatpakDir *self,
|
||||
GRegex **deny_regex,
|
||||
GError **error);
|
||||
|
||||
static char *flatpak_dir_get_remote_signature_lookaside (FlatpakDir *self,
|
||||
const char *remote_name);
|
||||
|
||||
static void ensure_http_session (FlatpakDir *self);
|
||||
|
||||
static void flatpak_dir_log (FlatpakDir *self,
|
||||
@@ -1200,15 +1203,17 @@ lookup_oci_registry_uri_from_summary (GVariant *summary,
|
||||
}
|
||||
|
||||
static FlatpakImageSource *
|
||||
flatpak_remote_state_new_image_source (FlatpakRemoteState *self,
|
||||
const char *oci_repository,
|
||||
const char *digest,
|
||||
const char *token,
|
||||
GCancellable *cancellable,
|
||||
GError **error)
|
||||
flatpak_remote_state_new_image_source (FlatpakRemoteState *self,
|
||||
FlatpakDir *dir,
|
||||
const char *oci_repository,
|
||||
const char *digest,
|
||||
const char *token,
|
||||
GCancellable *cancellable,
|
||||
GError **error)
|
||||
{
|
||||
g_autofree char *registry_uri = NULL;
|
||||
g_autoptr(FlatpakImageSource) image_source = NULL;
|
||||
g_autofree char *signature_lookaside = NULL;
|
||||
|
||||
if (!flatpak_remote_state_ensure_summary (self, error))
|
||||
return NULL;
|
||||
@@ -1217,14 +1222,21 @@ flatpak_remote_state_new_image_source (FlatpakRemoteState *self,
|
||||
if (registry_uri == NULL)
|
||||
return NULL;
|
||||
|
||||
image_source = flatpak_image_source_new_remote (registry_uri, oci_repository, digest, token, NULL, error);
|
||||
signature_lookaside = flatpak_dir_get_remote_signature_lookaside (dir, self->remote_name);
|
||||
|
||||
image_source = flatpak_image_source_new_remote (registry_uri,
|
||||
oci_repository,
|
||||
digest,
|
||||
token,
|
||||
signature_lookaside,
|
||||
NULL, error);
|
||||
if (image_source == NULL)
|
||||
return NULL;
|
||||
|
||||
return g_steal_pointer (&image_source);
|
||||
}
|
||||
|
||||
static FlatpakImageSource *
|
||||
FlatpakImageSource *
|
||||
flatpak_remote_state_fetch_image_source (FlatpakRemoteState *self,
|
||||
FlatpakDir *dir,
|
||||
const char *ref,
|
||||
@@ -1262,7 +1274,7 @@ flatpak_remote_state_fetch_image_source (FlatpakRemoteState *self,
|
||||
|
||||
oci_digest = g_strconcat ("sha256:", opt_rev ? opt_rev : latest_rev, NULL);
|
||||
|
||||
image_source = flatpak_remote_state_new_image_source (self, oci_repository, oci_digest, token, cancellable, error);
|
||||
image_source = flatpak_remote_state_new_image_source (self, dir, oci_repository, oci_digest, token, cancellable, error);
|
||||
if (image_source == NULL)
|
||||
return NULL;
|
||||
|
||||
@@ -6867,7 +6879,7 @@ flatpak_dir_pull_oci (FlatpakDir *self,
|
||||
|
||||
g_info ("Pulling OCI image %s", oci_digest);
|
||||
|
||||
checksum = flatpak_pull_from_oci (repo, image_source,
|
||||
checksum = flatpak_pull_from_oci (repo, image_source, NULL,
|
||||
state->remote_name, ref, flatpak_flags, oci_pull_progress_cb, progress, cancellable, error);
|
||||
|
||||
if (checksum == NULL)
|
||||
@@ -15297,6 +15309,21 @@ flatpak_dir_get_remote_disabled (FlatpakDir *self,
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static char *
|
||||
flatpak_dir_get_remote_signature_lookaside (FlatpakDir *self,
|
||||
const char *remote_name)
|
||||
{
|
||||
GKeyFile *config = flatpak_dir_get_repo_config (self);
|
||||
g_autofree char *group = get_group (remote_name);
|
||||
g_autofree char *signature_lookaside = NULL;
|
||||
|
||||
signature_lookaside = g_key_file_get_string (config, group, "xa.signature-lookaside", NULL);
|
||||
if (signature_lookaside == NULL || *signature_lookaside == '\0')
|
||||
return NULL;
|
||||
|
||||
return g_steal_pointer (&signature_lookaside);
|
||||
}
|
||||
|
||||
static char *
|
||||
flatpak_dir_get_remote_install_authenticator_name (FlatpakDir *self,
|
||||
const char *remote_name)
|
||||
|
||||
@@ -46,6 +46,7 @@ FlatpakImageSource *flatpak_image_source_new_remote (const char *uri,
|
||||
const char *oci_repository,
|
||||
const char *digest,
|
||||
const char *token,
|
||||
const char *signature_lookaside,
|
||||
GCancellable *cancellable,
|
||||
GError **error);
|
||||
FlatpakImageSource *flatpak_image_source_new_for_location (const char *location,
|
||||
|
||||
@@ -184,6 +184,7 @@ flatpak_image_source_new_remote (const char *uri,
|
||||
const char *oci_repository,
|
||||
const char *digest,
|
||||
const char *token,
|
||||
const char *signature_lookaside,
|
||||
GCancellable *cancellable,
|
||||
GError **error)
|
||||
{
|
||||
@@ -194,6 +195,7 @@ flatpak_image_source_new_remote (const char *uri,
|
||||
return NULL;
|
||||
|
||||
flatpak_oci_registry_set_token (registry, token);
|
||||
flatpak_oci_registry_set_signature_lookaside (registry, signature_lookaside);
|
||||
|
||||
return flatpak_image_source_new (registry, oci_repository, digest, cancellable, error);
|
||||
}
|
||||
|
||||
@@ -61,6 +61,8 @@ FlatpakOciRegistry * flatpak_oci_registry_new_for_archive (GFile *archi
|
||||
GError **error);
|
||||
void flatpak_oci_registry_set_token (FlatpakOciRegistry *self,
|
||||
const char *token);
|
||||
void flatpak_oci_registry_set_signature_lookaside (FlatpakOciRegistry *self,
|
||||
const char *signature_lookaside);
|
||||
gboolean flatpak_oci_registry_is_local (FlatpakOciRegistry *self);
|
||||
const char * flatpak_oci_registry_get_uri (FlatpakOciRegistry *self);
|
||||
FlatpakOciIndex * flatpak_oci_registry_load_index (FlatpakOciRegistry *self,
|
||||
@@ -164,16 +166,6 @@ gboolean flatpak_archive_read_open_fd_with_checksum (struct archive *a,
|
||||
GChecksum *checksum,
|
||||
GError **error);
|
||||
|
||||
GBytes *flatpak_oci_sign_data (GBytes *data,
|
||||
const gchar **okey_ids,
|
||||
const char *homedir,
|
||||
GError **error);
|
||||
|
||||
FlatpakOciSignature *flatpak_oci_verify_signature (OstreeRepo *repo,
|
||||
const char *remote_name,
|
||||
GBytes *signature,
|
||||
GError **error);
|
||||
|
||||
gboolean flatpak_oci_index_ensure_cached (FlatpakHttpSession *http_session,
|
||||
const char *uri,
|
||||
GFile *index,
|
||||
@@ -202,6 +194,7 @@ typedef void (*FlatpakOciPullProgress) (guint64 total_size,
|
||||
|
||||
char * flatpak_pull_from_oci (OstreeRepo *repo,
|
||||
FlatpakImageSource *image_source,
|
||||
FlatpakImageSource *opt_dst_image_source,
|
||||
const char *remote,
|
||||
const char *ref,
|
||||
FlatpakPullFlags flags,
|
||||
|
||||
@@ -30,6 +30,7 @@
|
||||
#include <archive_entry.h>
|
||||
#include "flatpak-image-source-private.h"
|
||||
#include "flatpak-oci-registry-private.h"
|
||||
#include "flatpak-oci-signatures-private.h"
|
||||
#include "flatpak-repo-utils-private.h"
|
||||
#include "flatpak-utils-base-private.h"
|
||||
#include "flatpak-utils-private.h"
|
||||
@@ -73,6 +74,7 @@ struct FlatpakOciRegistry
|
||||
GFile *archive;
|
||||
int tmp_dfd;
|
||||
char *token;
|
||||
char *signature_lookaside;
|
||||
|
||||
/* Local repos */
|
||||
int dfd;
|
||||
@@ -134,11 +136,12 @@ flatpak_oci_registry_finalize (GObject *object)
|
||||
|
||||
g_clear_pointer (&self->http_session, flatpak_http_session_free);
|
||||
g_clear_pointer (&self->base_uri, g_uri_unref);
|
||||
g_free (self->uri);
|
||||
g_free (self->token);
|
||||
g_clear_pointer (&self->uri, g_free);
|
||||
g_clear_pointer (&self->token, g_free);
|
||||
g_clear_object (&self->archive);
|
||||
g_clear_pointer (&self->tmp_dir, glnx_tmpdir_free);
|
||||
g_clear_pointer (&self->certificates, flatpak_certificates_free);
|
||||
g_clear_pointer (&self->signature_lookaside, g_free);
|
||||
|
||||
G_OBJECT_CLASS (flatpak_oci_registry_parent_class)->finalize (object);
|
||||
}
|
||||
@@ -288,6 +291,21 @@ flatpak_oci_registry_set_token (FlatpakOciRegistry *self,
|
||||
0, NULL, NULL);
|
||||
}
|
||||
|
||||
void
|
||||
flatpak_oci_registry_set_signature_lookaside (FlatpakOciRegistry *self,
|
||||
const char *signature_lookaside)
|
||||
{
|
||||
g_set_str (&self->signature_lookaside, signature_lookaside);
|
||||
|
||||
if (self->signature_lookaside != NULL)
|
||||
{
|
||||
size_t last = strlen (self->signature_lookaside) - 1;
|
||||
|
||||
if (self->signature_lookaside[last] == '/')
|
||||
self->signature_lookaside[last] = '\0';
|
||||
}
|
||||
}
|
||||
|
||||
FlatpakOciRegistry *
|
||||
flatpak_oci_registry_new (const char *uri,
|
||||
gboolean for_write,
|
||||
@@ -2321,6 +2339,91 @@ flatpak_oci_registry_find_delta_manifest (FlatpakOciRegistry *registry,
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static FlatpakOciSignatures *
|
||||
remote_load_signatures (FlatpakOciRegistry *self,
|
||||
const char *oci_repository,
|
||||
const char *digest,
|
||||
GCancellable *cancellable,
|
||||
GError **error)
|
||||
{
|
||||
g_autoptr(FlatpakOciSignatures) signatures = flatpak_oci_signatures_new ();
|
||||
g_autofree char *digest_algorithm = NULL;
|
||||
g_autofree char *digest_value = NULL;
|
||||
guint i;
|
||||
const char *colon;
|
||||
|
||||
if (self->signature_lookaside == NULL)
|
||||
return g_steal_pointer (&signatures);
|
||||
|
||||
/*
|
||||
* Look for signatures via the containers/image separate storage protocol:
|
||||
*
|
||||
* https://github.com/containers/image/blob/main/docs/signature-protocols.md
|
||||
*/
|
||||
|
||||
colon = strchr (digest, ':');
|
||||
if (colon == NULL)
|
||||
{
|
||||
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
|
||||
"can't parse digest %s", digest);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
digest_algorithm = g_strndup (digest, colon - digest);
|
||||
digest_value = g_strdup (colon + 1);
|
||||
|
||||
for (i = 1; i < G_MAXUINT; i++)
|
||||
{
|
||||
g_autoptr(GBytes) bytes = NULL;
|
||||
g_autoptr(GError) local_error = NULL;
|
||||
g_autofree char *uri_s = NULL;
|
||||
|
||||
uri_s = g_strdup_printf ("%s/%s@%s=%s/signature-%u", self->signature_lookaside,
|
||||
oci_repository, digest_algorithm, digest_value, i);
|
||||
|
||||
bytes = flatpak_load_uri (self->http_session,
|
||||
uri_s, FLATPAK_HTTP_FLAGS_ACCEPT_OCI,
|
||||
NULL,
|
||||
NULL, NULL, NULL,
|
||||
cancellable, &local_error);
|
||||
if (bytes == NULL)
|
||||
{
|
||||
if (g_error_matches (local_error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND))
|
||||
break;
|
||||
else
|
||||
{
|
||||
g_propagate_error (error, g_steal_pointer (&local_error));
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
g_info ("Found OCI signature at %s", uri_s);
|
||||
flatpak_oci_signatures_add_signature (signatures, g_steal_pointer (&bytes));
|
||||
}
|
||||
|
||||
return g_steal_pointer (&signatures);
|
||||
}
|
||||
|
||||
static FlatpakOciSignatures *
|
||||
flatpak_oci_registry_load_signatures (FlatpakOciRegistry *self,
|
||||
const char *oci_repository,
|
||||
const char *digest,
|
||||
GCancellable *cancellable,
|
||||
GError **error)
|
||||
{
|
||||
if (self->dfd != -1)
|
||||
{
|
||||
g_autoptr(FlatpakOciSignatures) signatures = flatpak_oci_signatures_new ();
|
||||
|
||||
if (!flatpak_oci_signatures_load_from_dfd (signatures, self->dfd, cancellable, error))
|
||||
return NULL;
|
||||
|
||||
return g_steal_pointer (&signatures);
|
||||
}
|
||||
else
|
||||
return remote_load_signatures (self, oci_repository, digest, cancellable, error);
|
||||
}
|
||||
|
||||
static const char *
|
||||
get_image_metadata (FlatpakOciIndexImage *img, const char *key)
|
||||
{
|
||||
@@ -3079,6 +3182,7 @@ flatpak_mirror_image_from_oci (FlatpakOciRegistry *dst_registry,
|
||||
OstreeRepoCommitState old_state = 0;
|
||||
g_autofree char *old_diffid = NULL;
|
||||
g_autoptr(FlatpakOciIndex) index = NULL;
|
||||
g_autoptr(FlatpakOciSignatures) signatures = NULL;
|
||||
int n_layers;
|
||||
int i;
|
||||
|
||||
@@ -3179,12 +3283,21 @@ flatpak_mirror_image_from_oci (FlatpakOciRegistry *dst_registry,
|
||||
if (!flatpak_oci_registry_save_index (dst_registry, index, cancellable, error))
|
||||
return FALSE;
|
||||
|
||||
signatures = flatpak_oci_registry_load_signatures (registry, oci_repository, digest,
|
||||
cancellable, error);
|
||||
if (!signatures)
|
||||
return FALSE;
|
||||
|
||||
if (!flatpak_oci_signatures_save_to_dfd (signatures, dst_registry->dfd, cancellable, error))
|
||||
return FALSE;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
char *
|
||||
flatpak_pull_from_oci (OstreeRepo *repo,
|
||||
FlatpakImageSource *image_source,
|
||||
FlatpakImageSource *opt_dst_image_source,
|
||||
const char *remote,
|
||||
const char *ref,
|
||||
FlatpakPullFlags flags,
|
||||
@@ -3216,13 +3329,31 @@ flatpak_pull_from_oci (OstreeRepo *repo,
|
||||
FlatpakOciPullProgressData progress_data = { progress_cb, progress_user_data };
|
||||
g_autoptr(GVariantBuilder) metadata_builder = g_variant_builder_new (G_VARIANT_TYPE ("a{sv}"));
|
||||
g_autoptr(GVariant) metadata = NULL;
|
||||
g_autoptr(FlatpakOciSignatures) signatures = NULL;
|
||||
FlatpakOciRegistry *dst_registry = opt_dst_image_source ?
|
||||
flatpak_image_source_get_registry (opt_dst_image_source) : registry;
|
||||
const char *dest_oci_repository = opt_dst_image_source ?
|
||||
flatpak_image_source_get_oci_repository (opt_dst_image_source) : oci_repository;
|
||||
int n_layers;
|
||||
int i;
|
||||
|
||||
g_assert (g_str_has_prefix (digest, "sha256:"));
|
||||
|
||||
manifest_ref = flatpak_image_source_get_ref (image_source);
|
||||
signatures = flatpak_oci_registry_load_signatures (dst_registry,
|
||||
dest_oci_repository,
|
||||
digest,
|
||||
cancellable, error);
|
||||
if (!signatures)
|
||||
return FALSE;
|
||||
|
||||
if (!flatpak_oci_signatures_verify (signatures, repo, remote,
|
||||
dst_registry->uri,
|
||||
dest_oci_repository,
|
||||
digest,
|
||||
error))
|
||||
return FALSE;
|
||||
|
||||
manifest_ref = flatpak_image_source_get_ref (image_source);
|
||||
if (manifest_ref == NULL)
|
||||
{
|
||||
flatpak_fail_error (error, FLATPAK_ERROR_INVALID_DATA, _("No ref specified for OCI image %s"), digest);
|
||||
|
||||
@@ -9,9 +9,33 @@
|
||||
|
||||
typedef struct _FlatpakOciSignatures FlatpakOciSignatures;
|
||||
|
||||
FlatpakOciSignature *flatpak_oci_verify_signature (OstreeRepo *repo,
|
||||
const char *remote_name,
|
||||
GBytes *signature,
|
||||
GError **error);
|
||||
gboolean flatpak_remote_has_gpg_key (OstreeRepo *repo,
|
||||
const char *remote_name);
|
||||
|
||||
FlatpakOciSignatures *flatpak_oci_signatures_new (void);
|
||||
|
||||
void flatpak_oci_signatures_free (FlatpakOciSignatures *self);
|
||||
|
||||
void flatpak_oci_signatures_add_signature (FlatpakOciSignatures *self,
|
||||
GBytes *signature);
|
||||
|
||||
gboolean flatpak_oci_signatures_load_from_dfd (FlatpakOciSignatures *self,
|
||||
int dfd,
|
||||
GCancellable *cancellable,
|
||||
GError **error);
|
||||
|
||||
gboolean flatpak_oci_signatures_save_to_dfd (FlatpakOciSignatures *self,
|
||||
int dfd,
|
||||
GCancellable *cancellable,
|
||||
GError **error);
|
||||
gboolean flatpak_oci_signatures_verify (FlatpakOciSignatures *self,
|
||||
OstreeRepo *repo,
|
||||
const char *remote_name,
|
||||
const char *registry_url,
|
||||
const char *repository_name,
|
||||
const char *digest,
|
||||
GError **error);
|
||||
|
||||
G_DEFINE_AUTOPTR_CLEANUP_FUNC(FlatpakOciSignatures, flatpak_oci_signatures_free)
|
||||
|
||||
#endif /* __FLATPAK_OCI_SIGNATURES_H__ */
|
||||
|
||||
@@ -28,13 +28,26 @@
|
||||
#include <gpgme.h>
|
||||
|
||||
#include "flatpak-oci-signatures-private.h"
|
||||
#include "flatpak-uri-private.h"
|
||||
#include "flatpak-utils-private.h"
|
||||
|
||||
|
||||
G_DEFINE_AUTO_CLEANUP_FREE_FUNC (gpgme_data_t, gpgme_data_release, NULL)
|
||||
G_DEFINE_AUTO_CLEANUP_FREE_FUNC (gpgme_ctx_t, gpgme_release, NULL)
|
||||
G_DEFINE_AUTO_CLEANUP_FREE_FUNC (gpgme_key_t, gpgme_key_unref, NULL)
|
||||
|
||||
gboolean
|
||||
flatpak_remote_has_gpg_key (OstreeRepo *repo,
|
||||
const char *remote_name)
|
||||
{
|
||||
g_autoptr(GFile) keyring_file = NULL;
|
||||
g_autofree char *keyring_name = NULL;
|
||||
|
||||
keyring_name = g_strdup_printf ("%s.trustedkeys.gpg", remote_name);
|
||||
keyring_file = g_file_get_child (ostree_repo_get_path (repo), keyring_name);
|
||||
|
||||
return g_file_query_exists (keyring_file, NULL);
|
||||
}
|
||||
|
||||
static void
|
||||
flatpak_gpgme_error_to_gio_error (gpgme_error_t gpg_error,
|
||||
GError **error)
|
||||
@@ -158,7 +171,11 @@ flatpak_gpgme_ctx_tmp_home_dir (gpgme_ctx_t gpgme_ctx,
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
FlatpakOciSignature *
|
||||
/* WARNING: This verifies that the data is signed with the correct key, but
|
||||
* does not verify the payload matches what is being installed. The caller
|
||||
* must do that.
|
||||
*/
|
||||
static FlatpakOciSignature *
|
||||
flatpak_oci_verify_signature (OstreeRepo *repo,
|
||||
const char *remote_name,
|
||||
GBytes *signed_data,
|
||||
@@ -241,3 +258,208 @@ flatpak_oci_verify_signature (OstreeRepo *repo,
|
||||
return (FlatpakOciSignature *) g_steal_pointer (&json);
|
||||
}
|
||||
|
||||
struct _FlatpakOciSignatures
|
||||
{
|
||||
GPtrArray *signatures;
|
||||
};
|
||||
|
||||
FlatpakOciSignatures *
|
||||
flatpak_oci_signatures_new (void)
|
||||
{
|
||||
FlatpakOciSignatures *self = g_new0 (FlatpakOciSignatures, 1);
|
||||
|
||||
self->signatures = g_ptr_array_new_with_free_func ((GDestroyNotify)g_bytes_unref);
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
void
|
||||
flatpak_oci_signatures_free (FlatpakOciSignatures *self)
|
||||
{
|
||||
g_clear_pointer (&self->signatures, g_ptr_array_unref);
|
||||
g_free (self);
|
||||
}
|
||||
|
||||
void
|
||||
flatpak_oci_signatures_add_signature (FlatpakOciSignatures *self,
|
||||
GBytes *signature)
|
||||
{
|
||||
g_ptr_array_add (self->signatures, g_bytes_ref (signature));
|
||||
}
|
||||
|
||||
gboolean
|
||||
flatpak_oci_signatures_load_from_dfd (FlatpakOciSignatures *self,
|
||||
int dfd,
|
||||
GCancellable *cancellable,
|
||||
GError **error)
|
||||
{
|
||||
for (guint i = 1; i < G_MAXUINT; i++)
|
||||
{
|
||||
g_autofree char *filename = g_strdup_printf ("signature-%u", i);
|
||||
g_autoptr(GError) local_error = NULL;
|
||||
GBytes *signature;
|
||||
|
||||
signature = flatpak_load_file_at (dfd, filename, cancellable, &local_error);
|
||||
if (local_error)
|
||||
{
|
||||
if (g_error_matches (local_error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND))
|
||||
break;
|
||||
|
||||
g_propagate_error (error, g_steal_pointer (&local_error));
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
flatpak_oci_signatures_add_signature (self, g_steal_pointer (&signature));
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
gboolean
|
||||
flatpak_oci_signatures_save_to_dfd (FlatpakOciSignatures *self,
|
||||
int dfd,
|
||||
GCancellable *cancellable,
|
||||
GError **error)
|
||||
{
|
||||
for (guint i = 0; i < self->signatures->len; i++)
|
||||
{
|
||||
g_autofree char *filename = g_strdup_printf ("signature-%u", i + 1);
|
||||
GBytes *signature = g_ptr_array_index (self->signatures, i);
|
||||
gconstpointer data;
|
||||
gsize size;
|
||||
|
||||
data = g_bytes_get_data (signature, &size);
|
||||
|
||||
if (!glnx_file_replace_contents_at (dfd, filename, data, size,
|
||||
0, cancellable, error))
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/*
|
||||
* Strip the tag and or digest off of a docker-reference. Regular expressions
|
||||
* based off of:
|
||||
*
|
||||
* https://github.com/containers/image/tree/main/docker/reference
|
||||
*
|
||||
* For simplicity we don't do normalization - if the signature claims that it
|
||||
* matches 'ubuntu', we don't match turn that into docker.io/library/ubuntu
|
||||
* before matching against the domain and repository.
|
||||
*/
|
||||
|
||||
#define TAG "[0-9A-Za-z_][0-9A-Za-z_-]{0,127}"
|
||||
#define DIGEST "[A-Za-z][A-Za-z0-9]*(?:[-_+.][A-Za-z][A-Za-z0-9]*)*[:][[:xdigit:]]{32,}"
|
||||
#define STRIP_TAG_AND_DIGEST_RE "^(.*?)(?::" TAG ")?" "(?:@" DIGEST ")?$"
|
||||
|
||||
static char *
|
||||
reference_strip_tag_and_digest (const char *str)
|
||||
{
|
||||
static gsize regex = 0;
|
||||
g_autoptr(GMatchInfo) match_info = NULL;
|
||||
gboolean matched;
|
||||
|
||||
if (g_once_init_enter (®ex))
|
||||
{
|
||||
g_autoptr(GRegex) compiled = g_regex_new (STRIP_TAG_AND_DIGEST_RE,
|
||||
G_REGEX_DEFAULT,
|
||||
G_REGEX_MATCH_DEFAULT,
|
||||
NULL);
|
||||
g_assert (compiled);
|
||||
g_once_init_leave (®ex, (gsize) g_steal_pointer (&compiled));
|
||||
}
|
||||
|
||||
matched = g_regex_match ((GRegex *) regex, str,
|
||||
G_REGEX_MATCH_DEFAULT, &match_info);
|
||||
if (!matched)
|
||||
return NULL;
|
||||
|
||||
return g_match_info_fetch (match_info, 1);
|
||||
}
|
||||
|
||||
gboolean
|
||||
flatpak_oci_signatures_verify (FlatpakOciSignatures *self,
|
||||
OstreeRepo *repo,
|
||||
const char *remote_name,
|
||||
const char *registry_url,
|
||||
const char *repository_name,
|
||||
const char *digest,
|
||||
GError **error)
|
||||
{
|
||||
g_autoptr(GUri) uri = NULL;
|
||||
int port;
|
||||
g_autofree char *port_prefix = NULL;
|
||||
g_autofree char *expected_identity = NULL;
|
||||
|
||||
if (!flatpak_remote_has_gpg_key (repo, remote_name))
|
||||
{
|
||||
g_info ("%s: no GPG key, skipping verification", remote_name);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
uri = g_uri_parse (registry_url, FLATPAK_HTTP_URI_FLAGS | G_URI_FLAGS_PARSE_RELAXED, error);
|
||||
if (uri == NULL)
|
||||
return FALSE;
|
||||
|
||||
port = g_uri_get_port (uri);
|
||||
if (port != -1 && port != 80 && port != 443)
|
||||
port_prefix = g_strdup_printf (":%d", g_uri_get_port (uri));
|
||||
|
||||
expected_identity = g_strdup_printf ("%s%s/%s",
|
||||
g_uri_get_host (uri),
|
||||
port_prefix ? port_prefix : "",
|
||||
repository_name);
|
||||
|
||||
for (guint i = 0; i < self->signatures->len; i++)
|
||||
{
|
||||
g_autoptr(FlatpakOciSignature) signature = NULL;
|
||||
g_autoptr(GError) local_error = NULL;
|
||||
|
||||
signature = flatpak_oci_verify_signature (repo, remote_name,
|
||||
g_ptr_array_index (self->signatures, i),
|
||||
&local_error);
|
||||
|
||||
if (signature == NULL)
|
||||
{
|
||||
g_info ("Couldn't verify signature: %s", local_error->message);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (signature->critical.image.digest == NULL)
|
||||
{
|
||||
g_warning ("Signature is missing digest");
|
||||
continue;
|
||||
}
|
||||
|
||||
if (strcmp (signature->critical.image.digest, digest) != 0)
|
||||
{
|
||||
g_warning ("Digest in signature (%s) does not match %s",
|
||||
signature->critical.image.digest, digest);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (signature->critical.identity.reference != NULL)
|
||||
{
|
||||
g_autofree char *stripped = NULL;
|
||||
|
||||
stripped = reference_strip_tag_and_digest (signature->critical.identity.reference);
|
||||
|
||||
if (g_strcmp0 (expected_identity, stripped) != 0)
|
||||
{
|
||||
g_info ("Identity in signature (%s) does not match %s",
|
||||
signature->critical.identity.reference,
|
||||
expected_identity);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
g_info ("%s: found valid signature for %s@%s",
|
||||
remote_name, expected_identity, digest);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
return flatpak_fail_error (error, FLATPAK_ERROR_UNTRUSTED,
|
||||
"%s: no valid signatures for %s@%s",
|
||||
remote_name, expected_identity, digest);
|
||||
}
|
||||
|
||||
@@ -493,6 +493,7 @@ handle_deploy (FlatpakSystemHelper *object,
|
||||
const char *image_source_digest;
|
||||
const char *verified_digest;
|
||||
g_autofree char *upstream_url = NULL;
|
||||
g_autoptr(FlatpakImageSource) system_image_source = NULL;
|
||||
|
||||
ostree_repo_remote_get_url (flatpak_dir_get_repo (system),
|
||||
arg_origin,
|
||||
@@ -550,7 +551,21 @@ handle_deploy (FlatpakSystemHelper *object,
|
||||
return G_DBUS_METHOD_INVOCATION_HANDLED;
|
||||
}
|
||||
|
||||
checksum = flatpak_pull_from_oci (flatpak_dir_get_repo (system), image_source,
|
||||
system_image_source =
|
||||
flatpak_remote_state_fetch_image_source (state,
|
||||
system,
|
||||
arg_ref,
|
||||
verified_digest,
|
||||
NULL,
|
||||
NULL, &error);
|
||||
if (!system_image_source)
|
||||
{
|
||||
g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR, G_DBUS_ERROR_FAILED,
|
||||
"Can't fetch image source: %s", error->message);
|
||||
return G_DBUS_METHOD_INVOCATION_HANDLED;
|
||||
}
|
||||
|
||||
checksum = flatpak_pull_from_oci (flatpak_dir_get_repo (system), image_source, system_image_source,
|
||||
arg_origin, arg_ref, FLATPAK_PULL_FLAGS_NONE, NULL, NULL, NULL, &error);
|
||||
if (checksum == NULL)
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user