GVariant *summary;
GHashTable *summary_deltas_checksums;
GHashTable *ref_original_commits; /* Maps checksum to commit, used by timestamp checks */
+ GHashTable *gpg_verified_commits; /* Set<checksum> of commits that have been verified */
GPtrArray *static_delta_superblocks;
GHashTable *expected_commit_sizes; /* Maps commit checksum to known size */
GHashTable *commit_to_depth; /* Maps commit checksum maximum depth */
GHashTable *scanned_metadata; /* Maps object name to itself */
- GHashTable *fetched_detached_metadata; /* Set<checksum> */
+ GHashTable *fetched_detached_metadata; /* Map<checksum,GVariant> */
GHashTable *requested_metadata; /* Maps object name to itself */
GHashTable *requested_content; /* Maps checksum to itself */
GHashTable *requested_fallback_content; /* Maps checksum to itself */
OstreeCollectionRef *requested_ref; /* (nullable) */
} ScanObjectQueueData;
+static void
+variant_or_null_unref (gpointer data)
+{
+ if (data)
+ g_variant_unref (data);
+}
+
static void start_fetch (OtPullData *pull_data, FetchObjectData *fetch);
static void start_fetch_deltapart (OtPullData *pull_data,
FetchStaticDeltaData *fetch);
GCancellable *cancellable,
GError **error);
static void scan_object_queue_data_free (ScanObjectQueueData *scan_data);
+static gboolean
+gpg_verify_unwritten_commit (OtPullData *pull_data,
+ const char *checksum,
+ GVariant *commit,
+ GVariant *detached_metadata,
+ GCancellable *cancellable,
+ GError **error);
+
static gboolean
update_progress (gpointer user_data)
/* Now that we've at least tried to fetch it, we can proceed to
* scan/fetch the commit object */
- g_hash_table_add (pull_data->fetched_detached_metadata, g_strdup (checksum));
+ g_hash_table_insert (pull_data->fetched_detached_metadata, g_strdup (checksum), NULL);
if (!fetch_data->object_is_stored)
enqueue_one_object_request (pull_data, checksum, objtype, fetch_data->path, FALSE, FALSE, fetch_data->requested_ref);
pull_data->cancellable, error))
goto out;
- g_hash_table_add (pull_data->fetched_detached_metadata, g_strdup (checksum));
+ g_hash_table_insert (pull_data->fetched_detached_metadata, g_strdup (checksum), g_steal_pointer (&metadata));
if (!fetch_data->object_is_stored)
enqueue_one_object_request (pull_data, checksum, objtype, fetch_data->path, FALSE, FALSE, fetch_data->requested_ref);
FALSE, &metadata, error))
goto out;
- /* Write the commitpartial file now while we're still fetching data */
+ /* For commit objects, compute the hash and check the GPG signature before
+ * writing to the repo, and also write the .commitpartial to say that
+ * we're still processing this commit.
+ */
if (objtype == OSTREE_OBJECT_TYPE_COMMIT)
{
+ /* Verify checksum */
+ OtChecksum hasher = { 0, };
+ ot_checksum_init (&hasher);
+ { g_autoptr(GBytes) bytes = g_variant_get_data_as_bytes (metadata);
+ ot_checksum_update_bytes (&hasher, bytes);
+ }
+ char hexdigest[OSTREE_SHA256_STRING_LEN+1];
+ ot_checksum_get_hexdigest (&hasher, hexdigest, sizeof (hexdigest));
+
+ if (strcmp (checksum, hexdigest) != 0)
+ {
+ (void) glnx_throw (error, "Corrupted %s object %s (actual checksum is %s)",
+ ostree_object_type_to_string (objtype),
+ checksum, hexdigest);
+ goto out;
+ }
+
+ /* Do GPG verification. `detached_data` may be NULL if no detached
+ * metadata was found during pull; that's handled by
+ * gpg_verify_unwritten_commit(). If we ever change the pull code to
+ * not always fetch detached metadata, this bit will have to learn how
+ * to look up from the disk state as well, or insert the on-disk
+ * metadata into this hash.
+ */
+ GVariant *detached_data = g_hash_table_lookup (pull_data->fetched_detached_metadata, checksum);
+ if (!gpg_verify_unwritten_commit (pull_data, checksum, metadata, detached_data,
+ pull_data->cancellable, error))
+ goto out;
+
if (!write_commitpartial_for (pull_data, checksum, error))
goto out;
}
if (!ostree_gpg_verify_result_require_valid_signature (result, error))
return FALSE;
+
+ /* We now check both *before* writing the commit, and after. Because the
+ * behavior used to be only verifiying after writing, we need to handle
+ * the case of "written but not verified". But we also don't want to check
+ * twice, as that'd result in duplicate signals.
+ */
+ g_hash_table_add (pull_data->gpg_verified_commits, g_strdup (checksum));
+
return TRUE;
}
{
if (pull_data->gpg_verify)
{
- g_autoptr(OstreeGpgVerifyResult) result = NULL;
- g_autoptr(GBytes) signed_data = g_variant_get_data_as_bytes (commit);
+ /* Shouldn't happen, but see comment in process_verify_result() */
+ if (g_hash_table_contains (pull_data->gpg_verified_commits, checksum))
+ return TRUE;
- result = _ostree_repo_gpg_verify_with_metadata (pull_data->repo,
- signed_data,
- detached_metadata,
- pull_data->remote_name,
- NULL, NULL,
- cancellable,
- error);
+ g_autoptr(GBytes) signed_data = g_variant_get_data_as_bytes (commit);
+ g_autoptr(OstreeGpgVerifyResult) result =
+ _ostree_repo_gpg_verify_with_metadata (pull_data->repo, signed_data,
+ detached_metadata, pull_data->remote_name,
+ NULL, NULL, cancellable, error);
if (!process_verify_result (pull_data, checksum, result, error))
return FALSE;
}
GINT_TO_POINTER (depth));
}
- if (pull_data->gpg_verify)
+ /* See comment in process_verify_result() - we now gpg check before writing,
+ * but also ensure we've done it here if not already.
+ */
+ if (pull_data->gpg_verify &&
+ !g_hash_table_contains (pull_data->gpg_verified_commits, checksum))
{
g_autoptr(OstreeGpgVerifyResult) result = NULL;
* add it to the hash to avoid re-fetching it below.
*/
if (objtype == OSTREE_OBJECT_TYPE_COMMIT)
- g_hash_table_add (pull_data->fetched_detached_metadata, g_strdup (checksum));
+ g_hash_table_insert (pull_data->fetched_detached_metadata, g_strdup (checksum), NULL);
pull_data->n_imported_metadata++;
is_stored = TRUE;
is_requested = TRUE;
return FALSE;
/* See comment above */
if (objtype == OSTREE_OBJECT_TYPE_COMMIT)
- g_hash_table_add (pull_data->fetched_detached_metadata, g_strdup (checksum));
+ g_hash_table_insert (pull_data->fetched_detached_metadata, g_strdup (checksum), NULL);
is_stored = TRUE;
is_requested = TRUE;
pull_data->n_imported_metadata++;
pull_data->ref_original_commits = g_hash_table_new_full (ostree_collection_ref_hash, ostree_collection_ref_equal,
(GDestroyNotify)NULL,
(GDestroyNotify)g_variant_unref);
+ pull_data->gpg_verified_commits = g_hash_table_new_full (g_str_hash, g_str_equal,
+ (GDestroyNotify)g_free, NULL);
pull_data->scanned_metadata = g_hash_table_new_full (ostree_hash_object_name, g_variant_equal,
(GDestroyNotify)g_variant_unref, NULL);
pull_data->fetched_detached_metadata = g_hash_table_new_full (g_str_hash, g_str_equal,
- (GDestroyNotify)g_free, NULL);
+ (GDestroyNotify)g_free, (GDestroyNotify)variant_or_null_unref);
pull_data->requested_content = g_hash_table_new_full (g_str_hash, g_str_equal,
(GDestroyNotify)g_free, NULL);
pull_data->requested_fallback_content = g_hash_table_new_full (g_str_hash, g_str_equal,
g_clear_pointer (&pull_data->fetched_detached_metadata, (GDestroyNotify) g_hash_table_unref);
g_clear_pointer (&pull_data->summary_deltas_checksums, (GDestroyNotify) g_hash_table_unref);
g_clear_pointer (&pull_data->ref_original_commits, (GDestroyNotify) g_hash_table_unref);
+ g_clear_pointer (&pull_data->gpg_verified_commits, (GDestroyNotify) g_hash_table_unref);
g_clear_pointer (&pull_data->requested_content, (GDestroyNotify) g_hash_table_unref);
g_clear_pointer (&pull_data->requested_fallback_content, (GDestroyNotify) g_hash_table_unref);
g_clear_pointer (&pull_data->requested_metadata, (GDestroyNotify) g_hash_table_unref);