From 7da9e0d25ba6036995183402231e7882e2bb985f Mon Sep 17 00:00:00 2001 From: Jan Grulich Date: Thu, 11 Jun 2026 10:51:42 +0000 Subject: [PATCH] Update to 1.18.0 Resolves: RHEL-126038 Resolves: RHEL-92123 Resolves: RHEL-165631 Resolves: RHEL-170158 --- .gitignore | 1 + ...try-on-401-with-FlatpakTokenProvider.patch | 970 ++++++++++++++++++ ...eck-signatures-from-mirrored-repo-in.patch | 457 +++++++++ ...crypto-policy-if-it-is-enabled-on-th.patch | 37 +- flatpak.spec | 15 +- sources | 2 +- 6 files changed, 1464 insertions(+), 18 deletions(-) create mode 100644 flatpak-oci-Retry-on-401-with-FlatpakTokenProvider.patch create mode 100644 flatpak-oci-registry-Check-signatures-from-mirrored-repo-in.patch diff --git a/.gitignore b/.gitignore index b1a8ccf..23090e9 100644 --- a/.gitignore +++ b/.gitignore @@ -117,3 +117,4 @@ /flatpak-1.16.0.tar.xz /flatpak-1.17.2.tar.xz /flatpak-1.17.3.tar.xz +/flatpak-1.18.0.tar.xz diff --git a/flatpak-oci-Retry-on-401-with-FlatpakTokenProvider.patch b/flatpak-oci-Retry-on-401-with-FlatpakTokenProvider.patch new file mode 100644 index 0000000..6519de6 --- /dev/null +++ b/flatpak-oci-Retry-on-401-with-FlatpakTokenProvider.patch @@ -0,0 +1,970 @@ +From 7ccc155aa363f389f52c7676b353ad5c9d6dcfc5 Mon Sep 17 00:00:00 2001 +From: Luigi Pavan +Date: Thu, 14 May 2026 12:27:57 +0200 +Subject: [PATCH 1/3] oci: Retry on 401 with FlatpakTokenProvider + +When a bearer token expires mid-transaction, OCI registry HTTP requests +fail with 401 Unauthorized, causing the entire transaction to fail. This +is particularly problematic for long-running transactions where the +token lifetime is shorter than the total operation time. + +Add a token refresh mechanism that allows the OCI registry layer to +transparently retry a failed HTTP request after obtaining a fresh token +via a FlatpakTokenProvider object provided by the transaction layer. + +FlatpakTokenProvider is a GObject that encapsulates the ability to +obtain and refresh bearer tokens. It holds references to the transaction +and the current operation, and its refresh_token method re-invokes +request_tokens_for_remote() to get a fresh token from the authenticator. +Its get_token method returns the current token from the operation, +providing a live view that reflects any refresh. + +FlatpakTokenProvider replaces the const char *token parameter on the +OCI code path functions, while leaving OSTree-only functions unchanged. +At registry creation points, the provider is set on the registry so it +can be used for retry on 401. + +At the FlatpakOciRegistry level, a token_provider field is added. On +401, the three HTTP call sites (remote_load_file, download_blob, +mirror_blob) attempt one token refresh and retry, defensively resetting +any output streams before the retry. +--- + common/flatpak-common-types-private.h | 1 + + common/flatpak-dir-private.h | 43 ++++---- + common/flatpak-dir.c | 80 +++++++------- + common/flatpak-image-source-private.h | 10 +- + common/flatpak-image-source.c | 20 ++-- + common/flatpak-oci-registry-private.h | 2 + + common/flatpak-oci-registry.c | 104 ++++++++++++++++--- + common/flatpak-transaction.c | 144 +++++++++++++++++++------- + 8 files changed, 288 insertions(+), 116 deletions(-) + +diff --git a/common/flatpak-common-types-private.h b/common/flatpak-common-types-private.h +index cb09d78e6d..49bb8bbeeb 100644 +--- a/common/flatpak-common-types-private.h ++++ b/common/flatpak-common-types-private.h +@@ -59,5 +59,6 @@ typedef struct _FlatpakImageSource FlatpakImageSource; + typedef struct FlatpakOciRegistry FlatpakOciRegistry; + typedef struct _FlatpakOciManifest FlatpakOciManifest; + typedef struct _FlatpakOciImage FlatpakOciImage; ++typedef struct _FlatpakTokenProvider FlatpakTokenProvider; + + #endif /* __FLATPAK_COMMON_TYPES_H__ */ +diff --git a/common/flatpak-dir-private.h b/common/flatpak-dir-private.h +index 9a9f8a12a6..431bb96c1a 100644 +--- a/common/flatpak-dir-private.h ++++ b/common/flatpak-dir-private.h +@@ -62,6 +62,13 @@ GType flatpak_deploy_get_type (void); + + typedef struct _PolkitSubject PolkitSubject; + ++#define FLATPAK_TYPE_TOKEN_PROVIDER flatpak_token_provider_get_type () ++G_DECLARE_FINAL_TYPE (FlatpakTokenProvider, flatpak_token_provider, FLATPAK, TOKEN_PROVIDER, GObject) ++ ++const char *flatpak_token_provider_get_token (FlatpakTokenProvider *self); ++gboolean flatpak_token_provider_refresh_token (FlatpakTokenProvider *self, ++ GCancellable *cancellable); ++ + typedef struct + { + FlatpakDecomposed *ref; +@@ -160,25 +167,25 @@ gboolean flatpak_remote_state_lookup_sparse_cache (FlatpakRemoteState *self, + const char *ref, + VarMetadataRef *out_metadata, + GError **error); +-GVariant *flatpak_remote_state_load_ref_commit (FlatpakRemoteState *self, +- FlatpakDir *dir, +- const char *ref, +- const char *opt_commit, +- const char *token, +- char **out_commit, +- GCancellable *cancellable, +- GError **error); ++GVariant *flatpak_remote_state_load_ref_commit (FlatpakRemoteState *self, ++ FlatpakDir *dir, ++ const char *ref, ++ const char *opt_commit, ++ FlatpakTokenProvider *token_provider, ++ char **out_commit, ++ GCancellable *cancellable, ++ GError **error); + 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); ++FlatpakImageSource * flatpak_remote_state_fetch_image_source (FlatpakRemoteState *self, ++ FlatpakDir *dir, ++ const char *ref, ++ const char *opt_rev, ++ FlatpakTokenProvider *token_provider, ++ GCancellable *cancellable, ++ GError **error); + + + G_DEFINE_AUTOPTR_CLEANUP_FUNC (FlatpakDir, g_object_unref) +@@ -611,7 +618,7 @@ gboolean flatpak_dir_pull (Fla + GFile *sideload_repo, + FlatpakImageSource *opt_image_source, + GBytes *require_metadata, +- const char *token, ++ FlatpakTokenProvider *token_provider, + OstreeRepo *repo, + FlatpakPullFlags flatpak_flags, + OstreeRepoPullFlags flags, +@@ -735,7 +742,7 @@ gboolean flatpak_dir_install (Fla + GFile *sideload_repo, + FlatpakImageSource *opt_image_source, + GBytes *require_metadata, +- const char *token, ++ FlatpakTokenProvider *token_provider, + FlatpakProgress *progress, + GCancellable *cancellable, + GError **error); +@@ -783,7 +790,7 @@ gboolean flatpak_dir_update (Fla + GFile *sideload_repo, + FlatpakImageSource *opt_image_source, + GBytes *require_metadata, +- const char *token, ++ FlatpakTokenProvider *token_provider, + FlatpakProgress *progress, + GCancellable *cancellable, + GError **error); +diff --git a/common/flatpak-dir.c b/common/flatpak-dir.c +index 73118e1127..da96153eb1 100644 +--- a/common/flatpak-dir.c ++++ b/common/flatpak-dir.c +@@ -116,7 +116,7 @@ G_DEFINE_AUTOPTR_CLEANUP_FUNC (AutoPolkitSubject, g_object_unref) + + static FlatpakOciRegistry *flatpak_dir_create_system_child_oci_registry (FlatpakDir *self, + GLnxLockFile *file_lock, +- const char *token, ++ FlatpakTokenProvider *token_provider, + GError **error); + + static OstreeRepo * flatpak_dir_create_child_repo (FlatpakDir *self, +@@ -135,7 +135,7 @@ static gboolean flatpak_dir_mirror_oci (FlatpakDir *self, + const char *ref, + const char *opt_rev, + FlatpakImageSource *opt_image_source, +- const char *token, ++ FlatpakTokenProvider *token_provider, + FlatpakProgress *progress, + GCancellable *cancellable, + GError **error); +@@ -1209,7 +1209,7 @@ flatpak_remote_state_new_image_source (FlatpakRemoteState *self, + FlatpakDir *dir, + const char *oci_repository, + const char *digest, +- const char *token, ++ FlatpakTokenProvider *token_provider, + GCancellable *cancellable, + GError **error) + { +@@ -1229,7 +1229,7 @@ flatpak_remote_state_new_image_source (FlatpakRemoteState *self, + image_source = flatpak_image_source_new_remote (registry_uri, + oci_repository, + digest, +- token, ++ token_provider, + signature_lookaside, + NULL, error); + if (image_source == NULL) +@@ -1243,7 +1243,7 @@ flatpak_remote_state_fetch_image_source (FlatpakRemoteState *self, + FlatpakDir *dir, + const char *ref, + const char *opt_rev, +- const char *token, ++ FlatpakTokenProvider *token_provider, + GCancellable *cancellable, + GError **error) + { +@@ -1277,7 +1277,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, dir, oci_repository, oci_digest, token, cancellable, error); ++ image_source = flatpak_remote_state_new_image_source (self, dir, oci_repository, oci_digest, token_provider, cancellable, error); + if (image_source == NULL) + return NULL; + +@@ -1302,13 +1302,13 @@ flatpak_remote_state_fetch_commit_object_oci (FlatpakRemoteState *self, + FlatpakDir *dir, + const char *ref, + const char *checksum, +- const char *token, ++ FlatpakTokenProvider *token_provider, + GCancellable *cancellable, + GError **error) + { + g_autoptr(FlatpakImageSource) image_source = NULL; + +- image_source = flatpak_remote_state_fetch_image_source (self, dir, ref, checksum, token, cancellable, error); ++ image_source = flatpak_remote_state_fetch_image_source (self, dir, ref, checksum, token_provider, cancellable, error); + if (image_source == NULL) + return NULL; + +@@ -1430,7 +1430,7 @@ flatpak_remote_state_load_ref_commit (FlatpakRemoteState *self, + FlatpakDir *dir, + const char *ref, + const char *opt_commit, +- const char *token, ++ FlatpakTokenProvider *token_provider, + char **out_commit, + GCancellable *cancellable, + GError **error) +@@ -1481,11 +1481,16 @@ flatpak_remote_state_load_ref_commit (FlatpakRemoteState *self, + } + + if (flatpak_dir_get_remote_oci (dir, self->remote_name)) +- commit_data = flatpak_remote_state_fetch_commit_object_oci (self, dir, ref, commit, token, +- cancellable, error); ++ { ++ commit_data = flatpak_remote_state_fetch_commit_object_oci (self, dir, ref, commit, token_provider, ++ cancellable, error); ++ } + else +- commit_data = flatpak_remote_state_fetch_commit_object (self, dir, ref, commit, token, +- cancellable, error); ++ { ++ const char *token = token_provider ? flatpak_token_provider_get_token (token_provider) : NULL; ++ commit_data = flatpak_remote_state_fetch_commit_object (self, dir, ref, commit, token, ++ cancellable, error); ++ } + + out: + if (out_commit) +@@ -6317,7 +6322,7 @@ flatpak_dir_setup_extra_data (FlatpakDir *self, + OstreeRepo *repo, + const char *ref, + const char *rev, +- const char *token, ++ FlatpakTokenProvider *token_provider, + FlatpakPullFlags flatpak_flags, + FlatpakProgress *progress, + GCancellable *cancellable, +@@ -6362,7 +6367,7 @@ flatpak_dir_setup_extra_data (FlatpakDir *self, + g_autoptr(GVariant) extra_data_sources = NULL; + + commitv = flatpak_remote_state_load_ref_commit (state, self, ref, rev, +- token, NULL, cancellable, error); ++ token_provider, NULL, cancellable, error); + if (commitv == NULL) + return FALSE; + +@@ -6838,7 +6843,7 @@ flatpak_dir_mirror_oci (FlatpakDir *self, + const char *ref, + const char *opt_rev, + FlatpakImageSource *opt_image_source, +- const char *token, ++ FlatpakTokenProvider *token_provider, + FlatpakProgress *progress, + GCancellable *cancellable, + GError **error) +@@ -6852,7 +6857,7 @@ flatpak_dir_mirror_oci (FlatpakDir *self, + if (!image_source) + { + image_source = flatpak_remote_state_fetch_image_source (state, self, +- ref, opt_rev, token, ++ ref, opt_rev, token_provider, + cancellable, error); + if (image_source == NULL) + return FALSE; +@@ -6887,7 +6892,7 @@ flatpak_dir_pull_oci (FlatpakDir *self, + OstreeRepo *repo, + FlatpakPullFlags flatpak_flags, + OstreeRepoPullFlags flags, +- const char *token, ++ FlatpakTokenProvider *token_provider, + FlatpakProgress *progress, + GCancellable *cancellable, + GError **error) +@@ -6907,7 +6912,7 @@ flatpak_dir_pull_oci (FlatpakDir *self, + if (!image_source) + { + image_source = flatpak_remote_state_fetch_image_source (state, self, +- ref, opt_rev, token, ++ ref, opt_rev, token_provider, + cancellable, error); + if (image_source == NULL) + return FALSE; +@@ -6935,7 +6940,7 @@ flatpak_dir_pull_oci (FlatpakDir *self, + g_info ("Imported OCI image as checksum %s", checksum); + + if (!flatpak_dir_setup_extra_data (self, state, repo, +- ref, checksum, token, ++ ref, checksum, token_provider, + flatpak_flags, + progress, + cancellable, +@@ -6976,7 +6981,7 @@ flatpak_dir_pull (FlatpakDir *self, + GFile *sideload_repo, + FlatpakImageSource *opt_image_source, + GBytes *require_metadata, +- const char *token, ++ FlatpakTokenProvider *token_provider, + OstreeRepo *repo, + FlatpakPullFlags flatpak_flags, + OstreeRepoPullFlags flags, +@@ -6992,6 +6997,7 @@ flatpak_dir_pull (FlatpakDir *self, + g_auto(GLnxLockFile) lock = { 0, }; + g_autofree char *name = NULL; + g_autofree char *current_checksum = NULL; ++ const char *token = token_provider ? flatpak_token_provider_get_token (token_provider) : NULL; + + if (!flatpak_dir_ensure_repo (self, cancellable, error)) + return FALSE; +@@ -7007,7 +7013,7 @@ flatpak_dir_pull (FlatpakDir *self, + + if (opt_image_source || flatpak_dir_get_remote_oci (self, state->remote_name)) + return flatpak_dir_pull_oci (self, state, ref, opt_rev, opt_image_source, repo, flatpak_flags, +- flags, token, progress, cancellable, error); ++ flags, token_provider, progress, cancellable, error); + + if (!ostree_repo_remote_get_url (self->repo, + state->remote_name, +@@ -7057,7 +7063,7 @@ flatpak_dir_pull (FlatpakDir *self, + /* Setup extra data information before starting to pull, so we can have precise + * progress reports */ + if (!flatpak_dir_setup_extra_data (self, state, repo, +- ref, rev, token, ++ ref, rev, token_provider, + flatpak_flags, + progress, + cancellable, +@@ -10483,7 +10489,7 @@ rewrite_dynamic_launchers (FlatpakDecomposed *ref, + static FlatpakOciRegistry * + flatpak_dir_create_system_child_oci_registry (FlatpakDir *self, + GLnxLockFile *file_lock, +- const char *token, ++ FlatpakTokenProvider *token_provider, + GError **error) + { + g_autoptr(GFile) cache_dir = NULL; +@@ -10518,7 +10524,11 @@ flatpak_dir_create_system_child_oci_registry (FlatpakDir *self, + if (new_registry == NULL) + return NULL; + +- flatpak_oci_registry_set_token (new_registry, token); ++ if (token_provider) ++ { ++ flatpak_oci_registry_set_token (new_registry, flatpak_token_provider_get_token (token_provider)); ++ flatpak_oci_registry_set_token_provider (new_registry, token_provider); ++ } + + return g_steal_pointer (&new_registry); + } +@@ -10800,7 +10810,7 @@ flatpak_dir_install (FlatpakDir *self, + GFile *sideload_repo, + FlatpakImageSource *opt_image_source, + GBytes *require_metadata, +- const char *token, ++ FlatpakTokenProvider *token_provider, + FlatpakProgress *progress, + GCancellable *cancellable, + GError **error) +@@ -10855,7 +10865,7 @@ flatpak_dir_install (FlatpakDir *self, + g_autoptr(FlatpakOciRegistry) registry = NULL; + g_autoptr(GFile) registry_file = NULL; + +- registry = flatpak_dir_create_system_child_oci_registry (self, &child_repo_lock, token, error); ++ registry = flatpak_dir_create_system_child_oci_registry (self, &child_repo_lock, token_provider, error); + if (registry == NULL) + return FALSE; + +@@ -10864,7 +10874,7 @@ flatpak_dir_install (FlatpakDir *self, + child_repo_path = g_file_get_path (registry_file); + + if (!flatpak_dir_mirror_oci (self, registry, state, flatpak_decomposed_get_ref (ref), +- opt_commit, opt_image_source, token, progress, cancellable, error)) ++ opt_commit, opt_image_source, token_provider, progress, cancellable, error)) + return FALSE; + } + else if (!gpg_verify_summary || !gpg_verify) +@@ -10958,7 +10968,7 @@ flatpak_dir_install (FlatpakDir *self, + + flatpak_flags |= FLATPAK_PULL_FLAGS_SIDELOAD_EXTRA_DATA; + +- if (!flatpak_dir_pull (self, state, flatpak_decomposed_get_ref (ref), opt_commit, subpaths, sideload_repo, NULL, require_metadata, token, ++ if (!flatpak_dir_pull (self, state, flatpak_decomposed_get_ref (ref), opt_commit, subpaths, sideload_repo, NULL, require_metadata, token_provider, + child_repo, + flatpak_flags, + 0, +@@ -11037,7 +11047,7 @@ flatpak_dir_install (FlatpakDir *self, + if (!no_pull) + { + if (!flatpak_dir_pull (self, state, flatpak_decomposed_get_ref (ref), opt_commit, opt_subpaths, +- sideload_repo, opt_image_source, require_metadata, token, NULL, ++ sideload_repo, opt_image_source, require_metadata, token_provider, NULL, + flatpak_flags, OSTREE_REPO_PULL_FLAGS_NONE, + progress, cancellable, error)) + return FALSE; +@@ -11512,7 +11522,7 @@ flatpak_dir_update (FlatpakDir *self, + GFile *sideload_repo, + FlatpakImageSource *opt_image_source, + GBytes *require_metadata, +- const char *token, ++ FlatpakTokenProvider *token_provider, + FlatpakProgress *progress, + GCancellable *cancellable, + GError **error) +@@ -11589,7 +11599,7 @@ flatpak_dir_update (FlatpakDir *self, + g_autoptr(FlatpakOciRegistry) registry = NULL; + g_autoptr(GFile) registry_file = NULL; + +- registry = flatpak_dir_create_system_child_oci_registry (self, &child_repo_lock, token, error); ++ registry = flatpak_dir_create_system_child_oci_registry (self, &child_repo_lock, token_provider, error); + if (registry == NULL) + return FALSE; + +@@ -11598,7 +11608,7 @@ flatpak_dir_update (FlatpakDir *self, + child_repo_path = g_file_get_path (registry_file); + + if (!flatpak_dir_mirror_oci (self, registry, state, flatpak_decomposed_get_ref (ref), +- commit, opt_image_source, token, progress, cancellable, error)) ++ commit, opt_image_source, token_provider, progress, cancellable, error)) + return FALSE; + } + else if (!gpg_verify_summary || !gpg_verify) +@@ -11678,7 +11688,7 @@ flatpak_dir_update (FlatpakDir *self, + + flatpak_flags |= FLATPAK_PULL_FLAGS_SIDELOAD_EXTRA_DATA; + if (!flatpak_dir_pull (self, state, flatpak_decomposed_get_ref (ref), +- commit, subpaths, sideload_repo, NULL, require_metadata, token, ++ commit, subpaths, sideload_repo, NULL, require_metadata, token_provider, + child_repo, + flatpak_flags, 0, + progress, cancellable, error)) +@@ -11744,7 +11754,7 @@ flatpak_dir_update (FlatpakDir *self, + if (!no_pull) + { + if (!flatpak_dir_pull (self, state, flatpak_decomposed_get_ref (ref), +- commit, subpaths, sideload_repo, opt_image_source, require_metadata, token, ++ commit, subpaths, sideload_repo, opt_image_source, require_metadata, token_provider, + NULL, flatpak_flags, OSTREE_REPO_PULL_FLAGS_NONE, + progress, cancellable, error)) + return FALSE; +diff --git a/common/flatpak-image-source-private.h b/common/flatpak-image-source-private.h +index 1259c1348d..92f6ea7df2 100644 +--- a/common/flatpak-image-source-private.h ++++ b/common/flatpak-image-source-private.h +@@ -42,11 +42,11 @@ FlatpakImageSource *flatpak_image_source_new_local (GFile *file, + const char *reference, + GCancellable *cancellable, + GError **error); +-FlatpakImageSource *flatpak_image_source_new_remote (const char *uri, +- const char *oci_repository, +- const char *digest, +- const char *token, +- const char *signature_lookaside, ++FlatpakImageSource *flatpak_image_source_new_remote (const char *uri, ++ const char *oci_repository, ++ const char *digest, ++ FlatpakTokenProvider *token_provider, ++ const char *signature_lookaside, + GCancellable *cancellable, + GError **error); + FlatpakImageSource *flatpak_image_source_new_for_location (const char *location, +diff --git a/common/flatpak-image-source.c b/common/flatpak-image-source.c +index eab92a1b8e..adb81947a1 100644 +--- a/common/flatpak-image-source.c ++++ b/common/flatpak-image-source.c +@@ -180,13 +180,13 @@ flatpak_image_source_new_local (GFile *file, + } + + 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) ++flatpak_image_source_new_remote (const char *uri, ++ const char *oci_repository, ++ const char *digest, ++ FlatpakTokenProvider *token_provider, ++ const char *signature_lookaside, ++ GCancellable *cancellable, ++ GError **error) + { + g_autoptr(FlatpakOciRegistry) registry = NULL; + +@@ -194,7 +194,11 @@ flatpak_image_source_new_remote (const char *uri, + if (!registry) + return NULL; + +- flatpak_oci_registry_set_token (registry, token); ++ if (token_provider) ++ { ++ flatpak_oci_registry_set_token (registry, flatpak_token_provider_get_token (token_provider)); ++ flatpak_oci_registry_set_token_provider (registry, token_provider); ++ } + flatpak_oci_registry_set_signature_lookaside (registry, signature_lookaside); + + return flatpak_image_source_new (registry, oci_repository, digest, cancellable, error); +diff --git a/common/flatpak-oci-registry-private.h b/common/flatpak-oci-registry-private.h +index ca56eab396..2fc75bc2c8 100644 +--- a/common/flatpak-oci-registry-private.h ++++ b/common/flatpak-oci-registry-private.h +@@ -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_token_provider (FlatpakOciRegistry *self, ++ FlatpakTokenProvider *provider); + void flatpak_oci_registry_set_signature_lookaside (FlatpakOciRegistry *self, + const char *signature_lookaside); + gboolean flatpak_oci_registry_is_local (FlatpakOciRegistry *self); +diff --git a/common/flatpak-oci-registry.c b/common/flatpak-oci-registry.c +index 3a3fec3d46..429c32d244 100644 +--- a/common/flatpak-oci-registry.c ++++ b/common/flatpak-oci-registry.c +@@ -74,6 +74,7 @@ struct FlatpakOciRegistry + GFile *archive; + int tmp_dfd; + char *token; ++ FlatpakTokenProvider *token_provider; + char *signature_lookaside; + + /* Local repos */ +@@ -138,6 +139,7 @@ flatpak_oci_registry_finalize (GObject *object) + g_clear_pointer (&self->base_uri, g_uri_unref); + g_clear_pointer (&self->uri, g_free); + g_clear_pointer (&self->token, g_free); ++ g_clear_object (&self->token_provider); + g_clear_object (&self->archive); + g_clear_pointer (&self->tmp_dir, glnx_tmpdir_free); + g_clear_pointer (&self->certificates, flatpak_certificates_free); +@@ -291,6 +293,29 @@ flatpak_oci_registry_set_token (FlatpakOciRegistry *self, + 0, NULL, NULL); + } + ++void ++flatpak_oci_registry_set_token_provider (FlatpakOciRegistry *self, ++ FlatpakTokenProvider *provider) ++{ ++ g_set_object (&self->token_provider, provider); ++} ++ ++static gboolean ++flatpak_oci_registry_try_refresh_token (FlatpakOciRegistry *self, ++ GCancellable *cancellable) ++{ ++ if (self->token_provider == NULL) ++ return FALSE; ++ ++ g_info ("Refreshing registry token for %s", self->uri); ++ ++ if (!flatpak_token_provider_refresh_token (self->token_provider, cancellable)) ++ return FALSE; ++ ++ flatpak_oci_registry_set_token (self, flatpak_token_provider_get_token (self->token_provider)); ++ return TRUE; ++} ++ + void + flatpak_oci_registry_set_signature_lookaside (FlatpakOciRegistry *self, + const char *signature_lookaside) +@@ -384,6 +409,18 @@ remote_load_file (FlatpakOciRegistry *self, + NULL, self->token, + NULL, NULL, NULL, out_content_type, NULL, + cancellable, error); ++ if (bytes == NULL ++ && g_error_matches (*error, FLATPAK_HTTP_ERROR, FLATPAK_HTTP_ERROR_UNAUTHORIZED) ++ && flatpak_oci_registry_try_refresh_token (self, cancellable)) ++ { ++ g_clear_error (error); ++ bytes = flatpak_load_uri_full (self->http_session, ++ uri_s, self->certificates, FLATPAK_HTTP_FLAGS_ACCEPT_OCI, ++ NULL, self->token, ++ NULL, NULL, NULL, out_content_type, NULL, ++ cancellable, error); ++ } ++ + if (bytes == NULL) + return NULL; + +@@ -1016,13 +1053,34 @@ flatpak_oci_registry_download_blob (FlatpakOciRegistry *self, + if (fd == -1) + return -1; + +- if (!flatpak_download_http_uri (self->http_session, uri_s, +- self->certificates, +- FLATPAK_HTTP_FLAGS_ACCEPT_OCI, +- out_stream, +- self->token, +- progress_cb, user_data, +- cancellable, error)) ++ gboolean download_ok; ++ ++ download_ok = flatpak_download_http_uri (self->http_session, uri_s, ++ self->certificates, ++ FLATPAK_HTTP_FLAGS_ACCEPT_OCI, ++ out_stream, ++ self->token, ++ progress_cb, user_data, ++ cancellable, error); ++ if (!download_ok ++ && g_error_matches (*error, FLATPAK_HTTP_ERROR, FLATPAK_HTTP_ERROR_UNAUTHORIZED) ++ && flatpak_oci_registry_try_refresh_token (self, cancellable)) ++ { ++ g_clear_error (error); ++ g_clear_object (&out_stream); ++ ftruncate (fd, 0); ++ lseek (fd, 0, SEEK_SET); ++ out_stream = g_unix_output_stream_new (fd, FALSE); ++ ++ download_ok = flatpak_download_http_uri (self->http_session, uri_s, ++ self->certificates, ++ FLATPAK_HTTP_FLAGS_ACCEPT_OCI, ++ out_stream, ++ self->token, ++ progress_cb, user_data, ++ cancellable, error); ++ } ++ if (!download_ok) + return -1; + + if (!g_output_stream_close (out_stream, cancellable, error)) +@@ -1107,14 +1165,34 @@ flatpak_oci_registry_mirror_blob (FlatpakOciRegistry *self, + if (uri_s == NULL) + return FALSE; + ++ gboolean download_ok; ++ + out_stream = g_unix_output_stream_new (tmpf.fd, FALSE); + +- if (!flatpak_download_http_uri (source_registry->http_session, +- uri_s, source_registry->certificates, +- FLATPAK_HTTP_FLAGS_ACCEPT_OCI, out_stream, +- self->token, +- progress_cb, user_data, +- cancellable, error)) ++ download_ok = flatpak_download_http_uri (source_registry->http_session, ++ uri_s, source_registry->certificates, ++ FLATPAK_HTTP_FLAGS_ACCEPT_OCI, out_stream, ++ self->token, ++ progress_cb, user_data, ++ cancellable, error); ++ if (!download_ok ++ && g_error_matches (*error, FLATPAK_HTTP_ERROR, FLATPAK_HTTP_ERROR_UNAUTHORIZED) ++ && flatpak_oci_registry_try_refresh_token (self, cancellable)) ++ { ++ g_clear_error (error); ++ g_clear_object (&out_stream); ++ ftruncate (tmpf.fd, 0); ++ lseek (tmpf.fd, 0, SEEK_SET); ++ out_stream = g_unix_output_stream_new (tmpf.fd, FALSE); ++ ++ download_ok = flatpak_download_http_uri (source_registry->http_session, ++ uri_s, source_registry->certificates, ++ FLATPAK_HTTP_FLAGS_ACCEPT_OCI, out_stream, ++ self->token, ++ progress_cb, user_data, ++ cancellable, error); ++ } ++ if (!download_ok) + return FALSE; + + if (!g_output_stream_close (out_stream, cancellable, error)) +diff --git a/common/flatpak-transaction.c b/common/flatpak-transaction.c +index 5d1460ce60..cc17d58786 100644 +--- a/common/flatpak-transaction.c ++++ b/common/flatpak-transaction.c +@@ -164,6 +164,25 @@ typedef struct { + GVariant *results; + } RequestData; + ++/* Pointers are not ref'd; the provider never outlives the transaction or op. */ ++struct _FlatpakTokenProvider { ++ GObject parent; ++ FlatpakTransaction *transaction; ++ FlatpakTransactionOperation *op; ++}; ++ ++G_DEFINE_TYPE (FlatpakTokenProvider, flatpak_token_provider, G_TYPE_OBJECT) ++ ++static void ++flatpak_token_provider_class_init (FlatpakTokenProviderClass *klass) ++{ ++} ++ ++static void ++flatpak_token_provider_init (FlatpakTokenProvider *self) ++{ ++} ++ + typedef struct _FlatpakTransactionPrivate + { + GObject parent; +@@ -244,6 +263,8 @@ enum { + }; + + static gboolean op_may_need_token (FlatpakTransactionOperation *op); ++static FlatpakTokenProvider *flatpak_token_provider_new_for_op (FlatpakTransaction *transaction, ++ FlatpakTransactionOperation *op); + + static void flatpak_transaction_normalize_ops (FlatpakTransaction *self); + static gboolean request_required_tokens (FlatpakTransaction *self, +@@ -3910,9 +3931,12 @@ resolve_ops (FlatpakTransaction *self, + flatpak_remote_state_lookup_ref (state, flatpak_decomposed_get_ref (op->ref), + NULL, NULL, &op->summary_metadata, NULL, NULL, NULL); + ++ g_autoptr(FlatpakTokenProvider) token_provider = ++ flatpak_token_provider_new_for_op (self, op); ++ + commit_data = flatpak_remote_state_load_ref_commit (state, priv->dir, + flatpak_decomposed_get_ref (op->ref), +- checksum, /* initially NULL */ op->resolved_token, ++ checksum, token_provider, + NULL, NULL, &local_error); + if (commit_data == NULL) + { +@@ -4351,6 +4375,7 @@ request_tokens_for_remote (FlatpakTransaction *self, + + /* Allow sending empty tokens to mean no token needed */ + ++ g_free (op->resolved_token); + op->resolved_token = *token == 0 ? NULL : g_strdup (token); + op->requested_token = TRUE; + } +@@ -4358,6 +4383,41 @@ request_tokens_for_remote (FlatpakTransaction *self, + return TRUE; + } + ++static FlatpakTokenProvider * ++flatpak_token_provider_new_for_op (FlatpakTransaction *transaction, ++ FlatpakTransactionOperation *op) ++{ ++ FlatpakTokenProvider *self = g_object_new (FLATPAK_TYPE_TOKEN_PROVIDER, NULL); ++ ++ self->transaction = transaction; ++ self->op = op; ++ ++ return self; ++} ++ ++const char * ++flatpak_token_provider_get_token (FlatpakTokenProvider *self) ++{ ++ return self->op->resolved_token; ++} ++ ++gboolean ++flatpak_token_provider_refresh_token (FlatpakTokenProvider *self, ++ GCancellable *cancellable) ++{ ++ GList op_list = { .data = self->op, .next = NULL, .prev = NULL }; ++ g_autoptr(GError) local_error = NULL; ++ ++ if (!request_tokens_for_remote (self->transaction, self->op->remote, ++ &op_list, cancellable, &local_error)) ++ { ++ g_info ("Token refresh failed: %s", local_error->message); ++ return FALSE; ++ } ++ ++ return TRUE; ++} ++ + static gboolean + request_required_tokens (FlatpakTransaction *self, + const char *optional_remote, /* else all remotes */ +@@ -5093,24 +5153,29 @@ _run_op_kind (FlatpakTransaction *self, + op->resolved_metakey, &local_error)) + res = FALSE; + else +- res = flatpak_dir_install (priv->dir, +- priv->no_pull, +- priv->no_deploy, +- priv->disable_static_deltas, +- priv->reinstall, +- priv->max_op >= APP_UPDATE, +- op->pin_on_deploy, +- op->update_preinstalled_on_deploy, +- remote_state, op->ref, +- op->resolved_commit, +- (const char **) op->subpaths, +- (const char **) op->previous_ids, +- op->resolved_sideload_path, +- op->resolved_image_source, +- op->resolved_metadata, +- op->resolved_token, +- progress->progress_obj, +- cancellable, &local_error); ++ { ++ g_autoptr(FlatpakTokenProvider) token_provider = ++ flatpak_token_provider_new_for_op (self, op); ++ ++ res = flatpak_dir_install (priv->dir, ++ priv->no_pull, ++ priv->no_deploy, ++ priv->disable_static_deltas, ++ priv->reinstall, ++ priv->max_op >= APP_UPDATE, ++ op->pin_on_deploy, ++ op->update_preinstalled_on_deploy, ++ remote_state, op->ref, ++ op->resolved_commit, ++ (const char **) op->subpaths, ++ (const char **) op->previous_ids, ++ op->resolved_sideload_path, ++ op->resolved_image_source, ++ op->resolved_metadata, ++ token_provider, ++ progress->progress_obj, ++ cancellable, &local_error); ++ } + + flatpak_transaction_progress_done (progress); + +@@ -5167,24 +5232,29 @@ _run_op_kind (FlatpakTransaction *self, + (const char **) op->previous_ids, + cancellable, &local_error); + else +- res = flatpak_dir_update (priv->dir, +- priv->no_pull, +- priv->no_deploy, +- priv->disable_static_deltas, +- op->commit != NULL, /* Allow downgrade if we specify commit */ +- priv->max_op >= APP_UPDATE, +- priv->max_op == APP_INSTALL || priv->max_op == RUNTIME_INSTALL, +- remote_state, +- op->ref, +- op->resolved_commit, +- (const char **) op->subpaths, +- (const char **) op->previous_ids, +- op->resolved_sideload_path, +- op->resolved_image_source, +- op->resolved_metadata, +- op->resolved_token, +- progress->progress_obj, +- cancellable, &local_error); ++ { ++ g_autoptr(FlatpakTokenProvider) token_provider = ++ flatpak_token_provider_new_for_op (self, op); ++ ++ res = flatpak_dir_update (priv->dir, ++ priv->no_pull, ++ priv->no_deploy, ++ priv->disable_static_deltas, ++ op->commit != NULL, /* Allow downgrade if we specify commit */ ++ priv->max_op >= APP_UPDATE, ++ priv->max_op == APP_INSTALL || priv->max_op == RUNTIME_INSTALL, ++ remote_state, ++ op->ref, ++ op->resolved_commit, ++ (const char **) op->subpaths, ++ (const char **) op->previous_ids, ++ op->resolved_sideload_path, ++ op->resolved_image_source, ++ op->resolved_metadata, ++ token_provider, ++ progress->progress_obj, ++ cancellable, &local_error); ++ } + flatpak_transaction_progress_done (progress); + + /* Handle noop-updates */ + +From 4ae3855f058d4aeed6f50298268d40dcb0c75375 Mon Sep 17 00:00:00 2001 +From: Luigi Pavan +Date: Tue, 19 May 2026 11:55:00 +0200 +Subject: [PATCH 2/3] oci: Fix crash when error parameter is NULL in 401 retry + logic + +The 401 retry logic added in the previous commit dereferences the +GError **error parameter with *error without first checking that error +is non-NULL. When callers pass NULL for error (e.g. the _deltaindex +lookup), this causes a SIGSEGV. + +Add error != NULL guards to all three retry sites in remote_load_file, +flatpak_oci_registry_download_blob, and flatpak_oci_registry_mirror_blob. +--- + common/flatpak-oci-registry.c | 3 +++ + 1 file changed, 3 insertions(+) + +diff --git a/common/flatpak-oci-registry.c b/common/flatpak-oci-registry.c +index 429c32d244..5ff45995f4 100644 +--- a/common/flatpak-oci-registry.c ++++ b/common/flatpak-oci-registry.c +@@ -410,6 +410,7 @@ remote_load_file (FlatpakOciRegistry *self, + NULL, NULL, NULL, out_content_type, NULL, + cancellable, error); + if (bytes == NULL ++ && error != NULL + && g_error_matches (*error, FLATPAK_HTTP_ERROR, FLATPAK_HTTP_ERROR_UNAUTHORIZED) + && flatpak_oci_registry_try_refresh_token (self, cancellable)) + { +@@ -1063,6 +1064,7 @@ flatpak_oci_registry_download_blob (FlatpakOciRegistry *self, + progress_cb, user_data, + cancellable, error); + if (!download_ok ++ && error != NULL + && g_error_matches (*error, FLATPAK_HTTP_ERROR, FLATPAK_HTTP_ERROR_UNAUTHORIZED) + && flatpak_oci_registry_try_refresh_token (self, cancellable)) + { +@@ -1176,6 +1178,7 @@ flatpak_oci_registry_mirror_blob (FlatpakOciRegistry *self, + progress_cb, user_data, + cancellable, error); + if (!download_ok ++ && error != NULL + && g_error_matches (*error, FLATPAK_HTTP_ERROR, FLATPAK_HTTP_ERROR_UNAUTHORIZED) + && flatpak_oci_registry_try_refresh_token (self, cancellable)) + { + +From 7fc7350f6d1e24abef3e18cf6da792e28751c746 Mon Sep 17 00:00:00 2001 +From: Luigi Pavan +Date: Mon, 8 Jun 2026 18:55:15 +0200 +Subject: [PATCH 3/3] oci: Fix download_blob 401 retry writing to O_RDONLY fd + +The retry path after a 401 discarded the O_WRONLY out_stream and +wrapped the O_RDONLY fd as a new write stream. ftruncate() silently +failed on the read-only descriptor and subsequent writes returned +EBADF, leaving the temp file empty. checksum_fd() then computed the +SHA-256 of zero bytes, causing a digest mismatch and a failed install. + +Fix by using g_unix_output_stream_get_fd() to reach the O_WRONLY fd, +resetting it in place, and reusing the existing stream. Apply the +same truncate-and-seek pattern to mirror_blob, where the stream +destroy-and-recreate roundtrip was unnecessary. +--- + common/flatpak-oci-registry.c | 9 +++------ + 1 file changed, 3 insertions(+), 6 deletions(-) + +diff --git a/common/flatpak-oci-registry.c b/common/flatpak-oci-registry.c +index 5ff45995f4..10d96049b5 100644 +--- a/common/flatpak-oci-registry.c ++++ b/common/flatpak-oci-registry.c +@@ -1069,10 +1069,9 @@ flatpak_oci_registry_download_blob (FlatpakOciRegistry *self, + && flatpak_oci_registry_try_refresh_token (self, cancellable)) + { + g_clear_error (error); +- g_clear_object (&out_stream); +- ftruncate (fd, 0); +- lseek (fd, 0, SEEK_SET); +- out_stream = g_unix_output_stream_new (fd, FALSE); ++ int write_fd = g_unix_output_stream_get_fd (G_UNIX_OUTPUT_STREAM (out_stream)); ++ ftruncate (write_fd, 0); ++ lseek (write_fd, 0, SEEK_SET); + + download_ok = flatpak_download_http_uri (self->http_session, uri_s, + self->certificates, +@@ -1183,10 +1182,8 @@ flatpak_oci_registry_mirror_blob (FlatpakOciRegistry *self, + && flatpak_oci_registry_try_refresh_token (self, cancellable)) + { + g_clear_error (error); +- g_clear_object (&out_stream); + ftruncate (tmpf.fd, 0); + lseek (tmpf.fd, 0, SEEK_SET); +- out_stream = g_unix_output_stream_new (tmpf.fd, FALSE); + + download_ok = flatpak_download_http_uri (source_registry->http_session, + uri_s, source_registry->certificates, diff --git a/flatpak-oci-registry-Check-signatures-from-mirrored-repo-in.patch b/flatpak-oci-registry-Check-signatures-from-mirrored-repo-in.patch new file mode 100644 index 0000000..ce30bb4 --- /dev/null +++ b/flatpak-oci-registry-Check-signatures-from-mirrored-repo-in.patch @@ -0,0 +1,457 @@ +From d439efe6dd66f623aca58d58ed981520f300113d Mon Sep 17 00:00:00 2001 +From: Sebastian Wick +Date: Thu, 11 Jun 2026 17:24:11 +0200 +Subject: [PATCH 1/4] oci-registry: Load the certificates from an ImageSource + +--- + common/flatpak-oci-registry.c | 30 ++++++++++++++++-------------- + 1 file changed, 16 insertions(+), 14 deletions(-) + +diff --git a/common/flatpak-oci-registry.c b/common/flatpak-oci-registry.c +index 3a3fec3d46..0a442cd407 100644 +--- a/common/flatpak-oci-registry.c ++++ b/common/flatpak-oci-registry.c +@@ -2405,23 +2405,28 @@ remote_load_signatures (FlatpakOciRegistry *self, + } + + static FlatpakOciSignatures * +-flatpak_oci_registry_load_signatures (FlatpakOciRegistry *self, +- const char *oci_repository, +- const char *digest, +- GCancellable *cancellable, +- GError **error) ++load_signatures (FlatpakImageSource *image_source, ++ GCancellable *cancellable, ++ GError **error) + { +- if (self->dfd != -1) ++ FlatpakOciRegistry *registry = flatpak_image_source_get_registry (image_source); ++ ++ if (registry->dfd != -1) + { + g_autoptr(FlatpakOciSignatures) signatures = flatpak_oci_signatures_new (); + +- if (!flatpak_oci_signatures_load_from_dfd (signatures, self->dfd, cancellable, error)) ++ if (!flatpak_oci_signatures_load_from_dfd (signatures, registry->dfd, cancellable, error)) + return NULL; + + return g_steal_pointer (&signatures); + } + else +- return remote_load_signatures (self, oci_repository, digest, cancellable, error); ++ { ++ const char *oci_repository = flatpak_image_source_get_oci_repository (image_source); ++ const char *digest = flatpak_image_source_get_digest (image_source); ++ ++ return remote_load_signatures (registry, oci_repository, digest, cancellable, error); ++ } + } + + static const char * +@@ -3283,8 +3288,7 @@ 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); ++ signatures = load_signatures (image_source, cancellable, error); + if (!signatures) + return FALSE; + +@@ -3339,10 +3343,8 @@ flatpak_pull_from_oci (OstreeRepo *repo, + + g_assert (g_str_has_prefix (digest, "sha256:")); + +- signatures = flatpak_oci_registry_load_signatures (dst_registry, +- dest_oci_repository, +- digest, +- cancellable, error); ++ signatures = load_signatures (opt_dst_image_source ? opt_dst_image_source : image_source, ++ cancellable, error); + if (!signatures) + return FALSE; + + +From 780916961cb935b74c2fcaeed6e88b30e35d04e1 Mon Sep 17 00:00:00 2001 +From: Sebastian Wick +Date: Thu, 11 Jun 2026 17:27:47 +0200 +Subject: [PATCH 2/4] oci-registry: Check signatures from mirrored repo in the + system helper + +In flatpak_pull_from_oci we can be in in the system helper where we pull +the mirrored OCI image into the system repo. However, to fetch the +signatures in GPG signed repos, we used a remote OciImageSource created +through `flatpak_remote_state_fetch_image_source`. This caused fetching +some data from the registry which we don't want in the deploy method, +and also fails if a token is required to access the repo. + +This change fetches the signatures from the mirrored OCI repo instead of +pulling them from the remote OciImageSource. The signatures can come from +anywhere because we verify them against the GPG key in the system repo. + +The important bit is the change in `flatpak_pull_from_oci` where we now +pass in the local image_source to fetch the signatures from, and in the +system helper, where we get the right metadata to check the signatures +against (eventually ends up in `flatpak_oci_signatures_verify`). +--- + app/flatpak-builtins-build-import-bundle.c | 2 +- + common/flatpak-dir.c | 2 +- + common/flatpak-oci-registry-private.h | 3 ++- + common/flatpak-oci-registry.c | 16 ++++++------- + system-helper/flatpak-system-helper.c | 28 ++++++++++++++-------- + 5 files changed, 29 insertions(+), 22 deletions(-) + +diff --git a/app/flatpak-builtins-build-import-bundle.c b/app/flatpak-builtins-build-import-bundle.c +index 6b06073f30..5455bb5348 100644 +--- a/app/flatpak-builtins-build-import-bundle.c ++++ b/app/flatpak-builtins-build-import-bundle.c +@@ -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, NULL, ++ commit_checksum = flatpak_pull_from_oci (repo, image_source, NULL, NULL, NULL, + ref, FLATPAK_PULL_FLAGS_NONE, + NULL, NULL, cancellable, error); + if (commit_checksum == NULL) +diff --git a/common/flatpak-dir.c b/common/flatpak-dir.c +index 73118e1127..36d9b4314c 100644 +--- a/common/flatpak-dir.c ++++ b/common/flatpak-dir.c +@@ -6926,7 +6926,7 @@ flatpak_dir_pull_oci (FlatpakDir *self, + + g_info ("Pulling OCI image %s", oci_digest); + +- checksum = flatpak_pull_from_oci (repo, image_source, NULL, ++ checksum = flatpak_pull_from_oci (repo, image_source, NULL, NULL, + state->remote_name, ref, flatpak_flags, oci_pull_progress_cb, progress, cancellable, error); + + if (checksum == NULL) +diff --git a/common/flatpak-oci-registry-private.h b/common/flatpak-oci-registry-private.h +index ca56eab396..451f770861 100644 +--- a/common/flatpak-oci-registry-private.h ++++ b/common/flatpak-oci-registry-private.h +@@ -194,7 +194,8 @@ typedef void (*FlatpakOciPullProgress) (guint64 total_size, + + char * flatpak_pull_from_oci (OstreeRepo *repo, + FlatpakImageSource *image_source, +- FlatpakImageSource *opt_dst_image_source, ++ const char *opt_sigcheck_repository, ++ const char *opt_sigcheck_registry_uri, + const char *remote, + const char *ref, + FlatpakPullFlags flags, +diff --git a/common/flatpak-oci-registry.c b/common/flatpak-oci-registry.c +index 0a442cd407..07b194e0c8 100644 +--- a/common/flatpak-oci-registry.c ++++ b/common/flatpak-oci-registry.c +@@ -3301,7 +3301,8 @@ flatpak_mirror_image_from_oci (FlatpakOciRegistry *dst_registry, + char * + flatpak_pull_from_oci (OstreeRepo *repo, + FlatpakImageSource *image_source, +- FlatpakImageSource *opt_dst_image_source, ++ const char *opt_sigcheck_repository, ++ const char *opt_sigcheck_registry_uri, + const char *remote, + const char *ref, + FlatpakPullFlags flags, +@@ -3334,23 +3335,20 @@ flatpak_pull_from_oci (OstreeRepo *repo, + 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; ++ const char *sigcheck_registry_uri = opt_sigcheck_registry_uri ? opt_sigcheck_registry_uri : registry->uri; ++ const char *sigcheck_repository = opt_sigcheck_repository ? opt_sigcheck_repository : oci_repository; + int n_layers; + int i; + + g_assert (g_str_has_prefix (digest, "sha256:")); + +- signatures = load_signatures (opt_dst_image_source ? opt_dst_image_source : image_source, +- cancellable, error); ++ signatures = load_signatures (image_source, cancellable, error); + if (!signatures) + return FALSE; + + if (!flatpak_oci_signatures_verify (signatures, repo, remote, +- dst_registry->uri, +- dest_oci_repository, ++ sigcheck_registry_uri, ++ sigcheck_repository, + digest, + error)) + return FALSE; +diff --git a/system-helper/flatpak-system-helper.c b/system-helper/flatpak-system-helper.c +index 81c0801628..f6156b70a2 100644 +--- a/system-helper/flatpak-system-helper.c ++++ b/system-helper/flatpak-system-helper.c +@@ -492,6 +492,9 @@ handle_deploy (FlatpakSystemHelper *object, + const char *verified_digest; + g_autofree char *upstream_url = NULL; + g_autoptr(FlatpakImageSource) system_image_source = NULL; ++ g_autoptr(GVariant) metadata = NULL; ++ const char *sigcheck_repository = NULL; ++ g_autofree char *sigcheck_registry_uri = NULL; + + if (!ostree_repo_remote_get_url (flatpak_dir_get_repo (system), + arg_origin, +@@ -546,21 +549,26 @@ handle_deploy (FlatpakSystemHelper *object, + return G_DBUS_METHOD_INVOCATION_HANDLED; + } + +- system_image_source = +- flatpak_remote_state_fetch_image_source (state, +- system, +- arg_ref, +- verified_digest, +- NULL, +- NULL, &error); +- if (!system_image_source) ++ flatpak_remote_state_lookup_ref (state, arg_ref, ++ NULL, NULL, ++ &metadata, ++ NULL, NULL, NULL); ++ ++ if (!g_variant_lookup (metadata, "xa.oci-repository", "s", &sigcheck_repository)) ++ { ++ g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR, G_DBUS_ERROR_FAILED, ++ "Can't get the OCI repository from the summary"); ++ return G_DBUS_METHOD_INVOCATION_HANDLED; ++ } ++ ++ if (!ostree_repo_remote_get_url (flatpak_dir_get_repo (system), arg_origin, &sigcheck_registry_uri, NULL)) + { + g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR, G_DBUS_ERROR_FAILED, +- "Can't fetch image source: %s", error->message); ++ "Can't get the OCI registry URI"); + return G_DBUS_METHOD_INVOCATION_HANDLED; + } + +- checksum = flatpak_pull_from_oci (flatpak_dir_get_repo (system), image_source, system_image_source, ++ checksum = flatpak_pull_from_oci (flatpak_dir_get_repo (system), image_source, sigcheck_repository, sigcheck_registry_uri, + arg_origin, arg_ref, FLATPAK_PULL_FLAGS_NONE, NULL, NULL, NULL, &error); + if (checksum == NULL) + { + +From ef6554153e801311cf1e76c2ddb5fc1dbcc22121 Mon Sep 17 00:00:00 2001 +From: Luigi Pavan +Date: Thu, 11 Jun 2026 12:50:26 +0200 +Subject: [PATCH 3/4] tests/oci-registry: Add bearer token auth support to the + mock OCI registry + +Add infrastructure for testing OCI installations from auth-protected +registries. The mock server now supports requiring a bearer token on all +/v2/ requests, configurable via a new POST /testing-auth/configure admin +endpoint. The client gains a corresponding 'configure-auth' subcommand. + +Assisted-by: Cursor +--- + tests/oci-registry-client.py | 17 +++++++++++++++++ + tests/oci-registry-server.py | 25 +++++++++++++++++++++++++ + 2 files changed, 42 insertions(+) + +diff --git a/tests/oci-registry-client.py b/tests/oci-registry-client.py +index 3855af5454..a80fe1973c 100755 +--- a/tests/oci-registry-client.py ++++ b/tests/oci-registry-client.py +@@ -82,6 +82,19 @@ def run_delete_sig(args): + sys.exit(1) + + ++def run_configure_auth(args): ++ params = {"token": args.token} ++ query = urllib.parse.urlencode(params) ++ conn = get_conn(args) ++ path = "/testing-auth/configure?{query}".format(query=query) ++ conn.request("POST", path) ++ response = conn.getresponse() ++ if response.status != 200: ++ print(response.read(), file=sys.stderr) ++ print("Failed: status={}".format(response.status), file=sys.stderr) ++ sys.exit(1) ++ ++ + parser = argparse.ArgumentParser() + parser.add_argument("--url", required=True) + parser.add_argument("--cacert") +@@ -114,5 +127,9 @@ def run_delete_sig(args): + delete_sig_parser.add_argument("digest") + delete_sig_parser.set_defaults(func=run_delete_sig) + ++configure_auth_parser = subparsers.add_parser("configure-auth") ++configure_auth_parser.add_argument("--token", required=True) ++configure_auth_parser.set_defaults(func=run_configure_auth) ++ + args = parser.parse_args() + args.func(args) +diff --git a/tests/oci-registry-server.py b/tests/oci-registry-server.py +index 497d7ca011..a890a097b1 100755 +--- a/tests/oci-registry-server.py ++++ b/tests/oci-registry-server.py +@@ -15,6 +15,8 @@ + icons = {} + signatures = {} + ++required_token = None ++ + + def get_index(): + results = [] +@@ -81,6 +83,15 @@ def check_route(self, route): + + return True + ++ def check_auth(self): ++ """Return True if auth is not required or the Authorization: Bearer header matches the required token.""" ++ if required_token is None: ++ return True ++ auth_header = self.headers.get("Authorization", "") ++ if not auth_header.startswith("Bearer "): ++ return False ++ return auth_header[len("Bearer "):] == required_token ++ + def do_GET(self): + response = 404 + response_string = b"" +@@ -97,10 +108,18 @@ def get_file_contents(repo_name, type, ref): + return 404, b"" + + if self.check_route("/v2/@repo_name/blobs/@digest"): ++ if not self.check_auth(): ++ self.send_response(401) ++ self.end_headers() ++ return + repo_name = self.matches["repo_name"] + digest = self.matches["digest"] + response, response_string = get_file_contents(repo_name, "blobs", digest) + elif self.check_route("/v2/@repo_name/manifests/@ref"): ++ if not self.check_auth(): ++ self.send_response(401) ++ self.end_headers() ++ return + repo_name = self.matches["repo_name"] + ref = self.matches["ref"] + response, response_string = get_file_contents(repo_name, "manifests", ref) +@@ -234,6 +253,12 @@ def do_POST(self): + ref = f"{repo_name}@{digest}" + sigs = signatures.setdefault(ref, []) + sigs.append(signature_bytes) ++ self.send_response(200) ++ self.end_headers() ++ elif self.check_route("/testing-auth/configure"): ++ global required_token ++ required_token = self.query.get("token", [None])[0] ++ + self.send_response(200) + self.end_headers() + else: + +From 35efa19c2e4f46359ede239057bde3783e50267d Mon Sep 17 00:00:00 2001 +From: Luigi Pavan +Date: Thu, 11 Jun 2026 12:50:26 +0200 +Subject: [PATCH 4/4] tests: Add test for installing from an auth-protected OCI + registry + +Add a regression test that installs from an auth-protected OCI registry. +The registry requires a bearer token for all /v2/ requests; the client +authenticates via the mock test authenticator and the installation +completes successfully. The test runs for both user and system install +paths. + +Assisted-by: Cursor +--- + tests/meson.build | 1 + + tests/test-oci-auth.sh | 69 ++++++++++++++++++++++++++++++++++++++++++ + 2 files changed, 70 insertions(+) + create mode 100755 tests/test-oci-auth.sh + +diff --git a/tests/meson.build b/tests/meson.build +index 43d1493b73..d0b4f29e06 100644 +--- a/tests/meson.build ++++ b/tests/meson.build +@@ -396,6 +396,7 @@ tests = { + 'system,http', + 'system,https', + ]}, ++ 'oci-auth': {'wrap': ['user', 'system']}, + 'update-remote-configuration': {'wrap': ['newsummary', 'oldsummary']}, + 'override': {}, + 'update-portal': {'wrap': ['user', 'system']}, +diff --git a/tests/test-oci-auth.sh b/tests/test-oci-auth.sh +new file mode 100755 +index 0000000000..33c2271f09 +--- /dev/null ++++ b/tests/test-oci-auth.sh +@@ -0,0 +1,69 @@ ++#!/bin/bash ++# ++# Copyright (C) 2026 Red Hat, Inc. ++# ++# This library is free software; you can redistribute it and/or ++# modify it under the terms of the GNU Lesser General Public ++# License as published by the Free Software Foundation; either ++# version 2 of the License, or (at your option) any later version. ++# ++# This library is distributed in the hope that it will be useful, ++# but WITHOUT ANY WARRANTY; without even the implied warranty of ++# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++# Lesser General Public License for more details. ++# ++# You should have received a copy of the GNU Lesser General Public ++# License along with this library; if not, write to the ++# Free Software Foundation, Inc., 59 Temple Place - Suite 330, ++# Boston, MA 02111-1307, USA. ++ ++set -euo pipefail ++ ++# shellcheck source=libtest.sh ++. "$(dirname "$0")/libtest.sh" ++ ++skip_without_bwrap ++ ++echo "1..1" ++ ++# Start the fake OCI registry server ++ ++httpd oci-registry-server.py --dir=. ++port=$(cat httpd-port) ++ ++# shellcheck disable=SC2154 ++client="python3 ${test_srcdir}/oci-registry-client.py --url=http://127.0.0.1:${port}" ++ ++setup_repo_no_add oci ++ ++# Build OCI bundles and upload them to the registry ++ ++${FLATPAK} build-bundle --runtime --oci $FL_GPGARGS repos/oci oci/platform-image org.test.Platform >&2 ++$client add platform latest "$(pwd)/oci/platform-image" ++ ++${FLATPAK} build-bundle --oci $FL_GPGARGS repos/oci oci/app-image org.test.Hello >&2 ++$client add hello latest "$(pwd)/oci/app-image" ++ ++# Add OCI remote configured to use the mock test authenticator. ++# The default authenticator for OCI remotes is org.flatpak.Authenticator.Oci; ++# we override it to use the mock authenticator that reads its token from ++# $XDG_RUNTIME_DIR/required-token on every call. ++ ++${FLATPAK} remote-add ${U} oci-auth-registry "oci+http://127.0.0.1:${port}" \ ++ --authenticator-name org.flatpak.Authenticator.test >&2 ++ ++# Test: install from an auth-protected OCI registry ++# ++# The registry requires a bearer token for all /v2/ requests. The client ++# authenticates via the mock authenticator and completes the installation ++# successfully. ++ ++echo -n "token-1" > "${XDG_RUNTIME_DIR}/required-token" ++$client configure-auth --token token-1 ++ ++${FLATPAK} ${U} install -y oci-auth-registry org.test.Hello >&2 ++ ++run org.test.Hello > hello_out ++assert_file_has_content hello_out '^Hello world, from a sandbox$' ++ ++ok "install from auth-protected OCI registry" diff --git a/flatpak-run-Enable-FIPS-crypto-policy-if-it-is-enabled-on-th.patch b/flatpak-run-Enable-FIPS-crypto-policy-if-it-is-enabled-on-th.patch index afdf34e..24bcbbc 100644 --- a/flatpak-run-Enable-FIPS-crypto-policy-if-it-is-enabled-on-th.patch +++ b/flatpak-run-Enable-FIPS-crypto-policy-if-it-is-enabled-on-th.patch @@ -1,4 +1,4 @@ -From 5f5aeea8d8be071468fb8e9640554518fb65885e Mon Sep 17 00:00:00 2001 +From 33de26dbeeda858042683db5f8d848a319622098 Mon Sep 17 00:00:00 2001 From: Sebastian Wick Date: Tue, 16 Dec 2025 17:15:32 +0100 Subject: [PATCH] run: Enable FIPS crypto policy if it is enabled on the host @@ -6,17 +6,17 @@ Subject: [PATCH] run: Enable FIPS crypto policy if it is enabled on the host This is a close copy of what podman/containers does to support FIPS. Any other crypto policy is ignored for now. --- - common/flatpak-run.c | 87 ++++++++++++++++++++++++++++++++++++++++++++ - 1 file changed, 87 insertions(+) + common/flatpak-run.c | 95 ++++++++++++++++++++++++++++++++++++++++++++ + 1 file changed, 95 insertions(+) -diff --git ./common/flatpak-run.c ../common/flatpak-run.c -index 6c319231..b51cc637 100644 ---- ./common/flatpak-run.c -+++ ../common/flatpak-run.c -@@ -2215,6 +2215,91 @@ flatpak_run_setup_usr_links (FlatpakBwrap *bwrap, +diff --git a/common/flatpak-run.c b/common/flatpak-run.c +index 5ea1250..7a9bf3c 100644 +--- a/common/flatpak-run.c ++++ b/common/flatpak-run.c +@@ -2312,6 +2312,91 @@ flatpak_run_setup_usr_links (FlatpakBwrap *bwrap, } } - + +static void +flatpak_run_setup_fips (FlatpakBwrap *bwrap, + GFile *runtime_files) @@ -105,15 +105,22 @@ index 6c319231..b51cc637 100644 /* Directories in /sys to share with the sandbox if accessible. */ static const char *const sysfs_dirs[] = { -@@ -2405,6 +2490,8 @@ flatpak_run_setup_base_argv (FlatpakBwrap *bwrap, +@@ -2524,6 +2609,16 @@ flatpak_run_setup_base_argv (FlatpakBwrap *bwrap, } } - -+ flatpak_run_setup_fips (bwrap, runtime_files); + ++ if (runtime_fd >= 0) ++ { ++ g_autofree char *runtime_path = flatpak_get_path_for_fd (runtime_fd, NULL); ++ if (runtime_path != NULL) ++ { ++ g_autoptr(GFile) runtime_files = g_file_new_for_path (runtime_path); ++ flatpak_run_setup_fips (bwrap, runtime_files); ++ } ++ } + if (app_id_dir != NULL) { g_autoptr(GFile) app_cache_dir = g_file_get_child (app_id_dir, "cache"); --- -2.51.0 - +-- +2.53.0 diff --git a/flatpak.spec b/flatpak.spec index 8b83d4b..3964a8e 100644 --- a/flatpak.spec +++ b/flatpak.spec @@ -12,7 +12,7 @@ %bcond malcontent %[!0%{?rhel}] Name: flatpak -Version: 1.17.3 +Version: 1.18.0 Release: 1%{?dist} Summary: Application deployment framework for desktop apps @@ -33,6 +33,10 @@ Source2: flatpak.sysusers.conf Patch1: flatpak-for-registry.redhat.io-get-certificates-from-etc-pki.patch # Enable FIPS support Patch2: flatpak-run-Enable-FIPS-crypto-policy-if-it-is-enabled-on-th.patch +# Retry on 401 with FlatpakTokenProvider (https://github.com/flatpak/flatpak/pull/6662) +Patch3: flatpak-oci-Retry-on-401-with-FlatpakTokenProvider.patch +# Check signatures from mirrored repo in system helper (https://github.com/flatpak/flatpak/pull/6682) +Patch4: flatpak-oci-registry-Check-signatures-from-mirrored-repo-in.patch # ostree not on i686 for RHEL 10 # https://github.com/containers/composefs/pull/229#issuecomment-1838735764 @@ -293,7 +297,7 @@ fi %files selinux %{_datadir}/selinux/packages/flatpak.pp.bz2 -%{_datadir}/selinux/devel/include/contrib/flatpak.if +%{_datadir}/selinux/devel/include/distributed/flatpak.if %files session-helper %license COPYING @@ -308,6 +312,13 @@ fi %changelog +* Thu Jun 11 2026 Jan Grulich - 1.18.0-1 +- Update to 1.18.0 + Resolves: RHEL-126038 + Resolves: RHEL-92123 + Resolves: RHEL-165631 + Resolves: RHEL-170158 + * Tue Mar 17 2026 Jan Grulich - 1.17.3-1 - 1.17.3 Resolves: RHEL-126038 diff --git a/sources b/sources index 6fb7977..c105aaf 100644 --- a/sources +++ b/sources @@ -1 +1 @@ -SHA512 (flatpak-1.17.3.tar.xz) = 889213e5babafbfc3bc2525d36351d0703a6f5da013c04010f1622236a59c8cea3cdef9430e31599dc0950c2da9621c172d2f6a359b5642273977787b9df1409 +SHA512 (flatpak-1.18.0.tar.xz) = 0dbfef463e3d2e7f9fd9db6c301804f1fd37450ef224936f9e68ed2d86bcbe5feaba99ead923bcd5f303bfc6376b858beb951c9f70afbbd65c4a0585ad2abc02