symbol_files = $(top_srcdir)/src/libostree/libostree-released.sym
# Uncomment this include when adding new development symbols.
-#if BUILDOPT_IS_DEVEL_BUILD
-#symbol_files += $(top_srcdir)/src/libostree/libostree-devel.sym
-#endif
+if BUILDOPT_IS_DEVEL_BUILD
+symbol_files += $(top_srcdir)/src/libostree/libostree-devel.sym
+endif
# http://blog.jgc.org/2007/06/escaping-comma-and-space-in-gnu-make.html
wl_versionscript_arg = -Wl,--version-script=
ostree_repo_remote_get_url
ostree_repo_remote_get_gpg_verify
ostree_repo_remote_get_gpg_verify_summary
+ostree_repo_remote_get_gpg_keys
ostree_repo_remote_gpg_import
ostree_repo_remote_fetch_summary
ostree_repo_remote_fetch_summary_with_options
OSTREE_REPO
OSTREE_IS_REPO
OSTREE_TYPE_REPO
+OSTREE_GPG_KEY_GVARIANT_STRING
+OSTREE_GPG_KEY_GVARIANT_FORMAT
ostree_repo_get_type
ostree_repo_commit_modifier_get_type
ostree_repo_transaction_stats_get_type
- uncomment the include in Makefile-libostree.am
*/
+LIBOSTREE_2021.4 {
+global:
+ ostree_repo_remote_get_gpg_keys;
+} LIBOSTREE_2021.3;
+
/* Stub section for the stable release *after* this development one; don't
* edit this other than to update the year. This is just a copy/paste
* source. Replace $LASTSTABLE with the last stable version, and $NEWVERSION
return ret;
}
+gboolean
+_ostree_gpg_verifier_list_keys (OstreeGpgVerifier *self,
+ const char * const *key_ids,
+ GPtrArray **out_keys,
+ GCancellable *cancellable,
+ GError **error)
+{
+ GLNX_AUTO_PREFIX_ERROR("GPG", error);
+ g_auto(gpgme_ctx_t) context = NULL;
+ g_autoptr(GOutputStream) pubring_stream = NULL;
+ g_autofree char *tmp_dir = NULL;
+ g_autoptr(GPtrArray) keys = NULL;
+ gpgme_error_t gpg_error = 0;
+ gboolean ret = FALSE;
+
+ if (g_cancellable_set_error_if_cancelled (cancellable, error))
+ goto out;
+
+ context = ot_gpgme_new_ctx (NULL, error);
+ if (context == NULL)
+ goto out;
+
+ if (!ot_gpgme_ctx_tmp_home_dir (context, &tmp_dir, &pubring_stream,
+ cancellable, error))
+ goto out;
+
+ if (!_ostree_gpg_verifier_import_keys (self, context, pubring_stream,
+ cancellable, error))
+ goto out;
+
+ keys = g_ptr_array_new_with_free_func ((GDestroyNotify) gpgme_key_unref);
+ if (key_ids != NULL)
+ {
+ for (guint i = 0; key_ids[i] != NULL; i++)
+ {
+ gpgme_key_t key = NULL;
+
+ gpg_error = gpgme_get_key (context, key_ids[i], &key, 0);
+ if (gpg_error != GPG_ERR_NO_ERROR)
+ {
+ ot_gpgme_throw (gpg_error, error, "Unable to find key \"%s\"",
+ key_ids[i]);
+ goto out;
+ }
+
+ /* Transfer ownership. */
+ g_ptr_array_add (keys, key);
+ }
+ }
+ else
+ {
+ gpg_error = gpgme_op_keylist_start (context, NULL, 0);
+ while (gpg_error == GPG_ERR_NO_ERROR)
+ {
+ gpgme_key_t key = NULL;
+
+ gpg_error = gpgme_op_keylist_next (context, &key);
+ if (gpg_error != GPG_ERR_NO_ERROR)
+ break;
+
+ /* Transfer ownership. */
+ g_ptr_array_add (keys, key);
+ }
+
+ if (gpgme_err_code (gpg_error) != GPG_ERR_EOF)
+ {
+ ot_gpgme_throw (gpg_error, error, "Unable to list keys");
+ goto out;
+ }
+ }
+
+ if (out_keys != NULL)
+ *out_keys = g_steal_pointer (&keys);
+
+ ret = TRUE;
+
+ out:
+ if (tmp_dir != NULL) {
+ ot_gpgme_kill_agent (tmp_dir);
+ (void) glnx_shutil_rm_rf_at (AT_FDCWD, tmp_dir, NULL, NULL);
+ }
+
+ return ret;
+}
+
OstreeGpgVerifyResult *
_ostree_gpg_verifier_check_signature (OstreeGpgVerifier *self,
GBytes *signed_data,
GCancellable *cancellable,
GError **error);
+gboolean _ostree_gpg_verifier_list_keys (OstreeGpgVerifier *self,
+ const char * const *key_ids,
+ GPtrArray **out_keys,
+ GCancellable *cancellable,
+ GError **error);
+
gboolean _ostree_gpg_verifier_add_keyring_dir (OstreeGpgVerifier *self,
GFile *path,
GCancellable *cancellable,
#endif /* OSTREE_DISABLE_GPGME */
}
+static gboolean
+_ostree_repo_gpg_prepare_verifier (OstreeRepo *self,
+ const gchar *remote_name,
+ GFile *keyringdir,
+ GFile *extra_keyring,
+ gboolean add_global_keyrings,
+ OstreeGpgVerifier **out_verifier,
+ GCancellable *cancellable,
+ GError **error);
+
+/**
+ * ostree_repo_remote_get_gpg_keys:
+ * @self: an #OstreeRepo
+ * @name (nullable): name of the remote or %NULL
+ * @key_ids: (array zero-terminated=1) (element-type utf8) (nullable):
+ * a %NULL-terminated array of GPG key IDs to include, or %NULL
+ * @out_keys: (out) (optional) (element-type GVariant) (transfer container):
+ * return location for a #GPtrArray of the remote's trusted GPG keys, or
+ * %NULL
+ * @cancellable: (nullable): a #GCancellable, or %NULL
+ * @error: return location for a #GError, or %NULL
+ *
+ * Enumerate the trusted GPG keys for the remote @name. If @name is
+ * %NULL, the global GPG keys will be returned. The keys will be
+ * returned in the @out_keys #GPtrArray. Each element in the array is a
+ * #GVariant of format %OSTREE_GPG_KEY_GVARIANT_FORMAT. The @key_ids
+ * array can be used to limit which keys are included. If @key_ids is
+ * %NULL, then all keys are included.
+ *
+ * Returns: %TRUE if the GPG keys could be enumerated, %FALSE otherwise
+ *
+ * Since: 2021.4
+ */
+gboolean
+ostree_repo_remote_get_gpg_keys (OstreeRepo *self,
+ const char *name,
+ const char * const *key_ids,
+ GPtrArray **out_keys,
+ GCancellable *cancellable,
+ GError **error)
+{
+#ifndef OSTREE_DISABLE_GPGME
+ g_autoptr(OstreeGpgVerifier) verifier = NULL;
+ gboolean global_keyrings = (name == NULL);
+ if (!_ostree_repo_gpg_prepare_verifier (self, name, NULL, NULL, global_keyrings,
+ &verifier, cancellable, error))
+ return FALSE;
+
+ g_autoptr(GPtrArray) gpg_keys = NULL;
+ if (!_ostree_gpg_verifier_list_keys (verifier, key_ids, &gpg_keys,
+ cancellable, error))
+ return FALSE;
+
+ g_autoptr(GPtrArray) keys =
+ g_ptr_array_new_with_free_func ((GDestroyNotify) g_variant_unref);
+ for (guint i = 0; i < gpg_keys->len; i++)
+ {
+ gpgme_key_t key = gpg_keys->pdata[i];
+
+ g_auto(GVariantBuilder) subkeys_builder = OT_VARIANT_BUILDER_INITIALIZER;
+ g_variant_builder_init (&subkeys_builder, G_VARIANT_TYPE ("a(a{sv})"));
+ g_auto(GVariantBuilder) uids_builder = OT_VARIANT_BUILDER_INITIALIZER;
+ g_variant_builder_init (&uids_builder, G_VARIANT_TYPE ("a(a{sv})"));
+ for (gpgme_subkey_t subkey = key->subkeys; subkey != NULL;
+ subkey = subkey->next)
+ {
+ g_auto(GVariantDict) subkey_dict = OT_VARIANT_BUILDER_INITIALIZER;
+ g_variant_dict_init (&subkey_dict, NULL);
+ g_variant_dict_insert_value (&subkey_dict, "fingerprint",
+ g_variant_new_string (subkey->fpr));
+ g_variant_dict_insert_value (&subkey_dict, "created",
+ g_variant_new_int64 (GINT64_TO_BE (subkey->timestamp)));
+ g_variant_dict_insert_value (&subkey_dict, "expires",
+ g_variant_new_int64 (GINT64_TO_BE (subkey->expires)));
+ g_variant_dict_insert_value (&subkey_dict, "revoked",
+ g_variant_new_boolean (subkey->revoked));
+ g_variant_dict_insert_value (&subkey_dict, "expired",
+ g_variant_new_boolean (subkey->expired));
+ g_variant_dict_insert_value (&subkey_dict, "invalid",
+ g_variant_new_boolean (subkey->invalid));
+ g_variant_builder_add (&subkeys_builder, "(@a{sv})",
+ g_variant_dict_end (&subkey_dict));
+ }
+
+ for (gpgme_user_id_t uid = key->uids; uid != NULL; uid = uid->next)
+ {
+ g_auto(GVariantDict) uid_dict = OT_VARIANT_BUILDER_INITIALIZER;
+ g_variant_dict_init (&uid_dict, NULL);
+ g_variant_dict_insert_value (&uid_dict, "uid",
+ g_variant_new_string (uid->uid));
+ g_variant_dict_insert_value (&uid_dict, "name",
+ g_variant_new_string (uid->name));
+ g_variant_dict_insert_value (&uid_dict, "comment",
+ g_variant_new_string (uid->comment));
+ g_variant_dict_insert_value (&uid_dict, "email",
+ g_variant_new_string (uid->email));
+ g_variant_dict_insert_value (&uid_dict, "revoked",
+ g_variant_new_boolean (uid->revoked));
+ g_variant_dict_insert_value (&uid_dict, "invalid",
+ g_variant_new_boolean (uid->invalid));
+ g_variant_builder_add (&uids_builder, "(@a{sv})",
+ g_variant_dict_end (&uid_dict));
+ }
+
+ /* Currently empty */
+ g_auto(GVariantDict) metadata_dict = OT_VARIANT_BUILDER_INITIALIZER;
+ g_variant_dict_init (&metadata_dict, NULL);
+
+ GVariant *key_variant = g_variant_new ("(@a(a{sv})@a(a{sv})@a{sv})",
+ g_variant_builder_end (&subkeys_builder),
+ g_variant_builder_end (&uids_builder),
+ g_variant_dict_end (&metadata_dict));
+ g_ptr_array_add (keys, g_variant_ref_sink (key_variant));
+ }
+
+ if (out_keys)
+ *out_keys = g_steal_pointer (&keys);
+
+ return TRUE;
+#else /* OSTREE_DISABLE_GPGME */
+ return glnx_throw (error, "GPG feature is disabled in a build time");
+#endif /* OSTREE_DISABLE_GPGME */
+}
+
/**
* ostree_repo_remote_fetch_summary:
* @self: Self
const char *name,
gboolean *out_gpg_verify_summary,
GError **error);
+
+/**
+ * OSTREE_GPG_KEY_GVARIANT_FORMAT:
+ *
+ * - a(a{sv}) - Array of subkeys. Each a{sv} dictionary represents a
+ * subkey. The primary key is the first subkey. The following keys are
+ * currently recognized:
+ * - key: `fingerprint`, value: `s`, key fingerprint hexadecimal string
+ * - key: `created`, value: `x`, key creation timestamp (seconds since
+ * the Unix epoch in UTC, big-endian)
+ * - key: `expires`, value: `x`, key expiration timestamp (seconds since
+ * the Unix epoch in UTC, big-endian). If this value is 0, the key does
+ * not expire.
+ * - key: `revoked`, value: `b`, whether key is revoked
+ * - key: `expired`, value: `b`, whether key is expired
+ * - key: `invalid`, value: `b`, whether key is invalid
+ * - a(a{sv}) - Array of user IDs. Each a{sv} dictionary represents a
+ * user ID. The following keys are currently recognized:
+ * - key: `uid`, value: `s`, full user ID (name, email and comment)
+ * - key: `name`, value: `s`, user ID name component
+ * - key: `comment`, value: `s`, user ID comment component
+ * - key: `email`, value: `s`, user ID email component
+ * - key: `revoked`, value: `b`, whether user ID is revoked
+ * - key: `invalid`, value: `b`, whether user ID is invalid
+ * - a{sv} - Additional metadata dictionary. There are currently no
+ * additional metadata keys defined.
+ *
+ * Since: 2021.4
+ */
+#define OSTREE_GPG_KEY_GVARIANT_STRING "(a(a{sv})a(a{sv})a{sv})"
+#define OSTREE_GPG_KEY_GVARIANT_FORMAT G_VARIANT_TYPE (OSTREE_GPG_KEY_GVARIANT_STRING)
+
+_OSTREE_PUBLIC
+gboolean ostree_repo_remote_get_gpg_keys (OstreeRepo *self,
+ const char *name,
+ const char * const *key_ids,
+ GPtrArray **out_keys,
+ GCancellable *cancellable,
+ GError **error);
+
_OSTREE_PUBLIC
gboolean ostree_repo_remote_gpg_import (OstreeRepo *self,
const char *name,