New upstream version 2021.4

This commit is contained in:
Simon McVittie committed 2021-10-05 21:01:43 +01:00
commit 085ba80d7c
90 files changed
+5415 -3764

No files matched your search

+7
View File
@@ -661,6 +661,13 @@ global:
ostree_repo_lock_pop;
} LIBOSTREE_2021.2;
LIBOSTREE_2021.4 {
global:
ostree_repo_remote_get_gpg_keys;
ostree_repo_signature_verify_commit_data;
} LIBOSTREE_2021.3;
/* NOTE: Only add more content here in release commits! See the
* comments at the top of this file.
*/
+8
View File
@@ -984,6 +984,9 @@ ostree_checksum_file_at (int dfd,
g_autoptr(GFileInfo) file_info = _ostree_stbuf_to_gfileinfo (stbuf);
const gboolean canonicalize_perms =
((flags & OSTREE_CHECKSUM_FLAGS_CANONICAL_PERMISSIONS) != 0);
g_autoptr(GInputStream) in = NULL;
if (S_ISREG (stbuf->st_mode))
{
@@ -991,6 +994,11 @@ ostree_checksum_file_at (int dfd,
if (!glnx_openat_rdonly (dfd, path, FALSE, &fd, error))
return FALSE;
in = g_unix_input_stream_new (glnx_steal_fd (&fd), TRUE);
if (canonicalize_perms)
{
g_file_info_set_attribute_uint32 (file_info, "unix::uid", 0);
g_file_info_set_attribute_uint32 (file_info, "unix::gid", 0);
}
}
else if (S_ISLNK (stbuf->st_mode))
{
+9
View File
@@ -460,12 +460,21 @@ gboolean ostree_break_hardlink (int dfd,
/**
* OstreeChecksumFlags:
* @OSTREE_CHECKSUM_FLAGS_NONE: Default checksumming without tweaks.
* (Since: 2017.13.)
* @OSTREE_CHECKSUM_FLAGS_IGNORE_XATTRS: Ignore xattrs when checksumming.
* (Since: 2017.13.)
* @OSTREE_CHECKSUM_FLAGS_CANONICAL_PERMISSIONS: Use canonical uid/gid/mode
* values, for bare-user-only mode. (Since: 2021.4.)
*
* Flags influencing checksumming logic.
*
* Since: 2017.13
*/
typedef enum {
OSTREE_CHECKSUM_FLAGS_NONE = 0,
OSTREE_CHECKSUM_FLAGS_IGNORE_XATTRS = (1 << 0),
OSTREE_CHECKSUM_FLAGS_CANONICAL_PERMISSIONS = (1 << 1),
} OstreeChecksumFlags;
_OSTREE_PUBLIC
+11 -8
View File
@@ -268,15 +268,18 @@ ostree_diff_dirs_with_options (OstreeDiffFlags flags,
/* If we're diffing versus a repo, and either of them have xattrs disabled,
* then disable for both.
*/
OstreeRepo *repo;
if (OSTREE_IS_REPO_FILE (a))
repo = ostree_repo_file_get_repo ((OstreeRepoFile*)a);
else if (OSTREE_IS_REPO_FILE (b))
repo = ostree_repo_file_get_repo ((OstreeRepoFile*)b);
else
repo = NULL;
if (repo != NULL && repo->disable_xattrs)
flags |= OSTREE_DIFF_FLAGS_IGNORE_XATTRS;
{
OstreeRepo *repo = ostree_repo_file_get_repo ((OstreeRepoFile*)a);
if (repo->disable_xattrs || repo->mode == OSTREE_REPO_MODE_BARE_USER_ONLY)
flags |= OSTREE_DIFF_FLAGS_IGNORE_XATTRS;
}
if (OSTREE_IS_REPO_FILE (b))
{
OstreeRepo *repo = ostree_repo_file_get_repo ((OstreeRepoFile*)b);
if (repo->disable_xattrs || repo->mode == OSTREE_REPO_MODE_BARE_USER_ONLY)
flags |= OSTREE_DIFF_FLAGS_IGNORE_XATTRS;
}
if (a == NULL)
{
+181 -77
View File
@@ -91,6 +91,185 @@ verify_result_finalized_cb (gpointer data,
(void) glnx_shutil_rm_rf_at (AT_FDCWD, tmp_dir, NULL, NULL);
}
static gboolean
_ostree_gpg_verifier_import_keys (OstreeGpgVerifier *self,
gpgme_ctx_t gpgme_ctx,
GOutputStream *pubring_stream,
GCancellable *cancellable,
GError **error)
{
GLNX_AUTO_PREFIX_ERROR("GPG", error);
for (GList *link = self->keyrings; link != NULL; link = link->next)
{
g_autoptr(GFileInputStream) source_stream = NULL;
GFile *keyring_file = link->data;
gssize bytes_written;
GError *local_error = NULL;
source_stream = g_file_read (keyring_file, cancellable, &local_error);
/* Disregard non-existent keyrings. */
if (g_error_matches (local_error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND))
{
g_clear_error (&local_error);
continue;
}
else if (local_error != NULL)
{
g_propagate_error (error, local_error);
return FALSE;
}
bytes_written = g_output_stream_splice (pubring_stream,
G_INPUT_STREAM (source_stream),
G_OUTPUT_STREAM_SPLICE_CLOSE_SOURCE,
cancellable, error);
if (bytes_written < 0)
return FALSE;
}
for (guint i = 0; i < self->keyring_data->len; i++)
{
GBytes *keyringd = self->keyring_data->pdata[i];
gsize len;
gsize bytes_written;
const guint8 *buf = g_bytes_get_data (keyringd, &len);
if (!g_output_stream_write_all (pubring_stream, buf, len, &bytes_written,
cancellable, error))
return FALSE;
}
if (!g_output_stream_close (pubring_stream, cancellable, error))
return FALSE;
/* Save the previous armor value - we need it on for importing ASCII keys */
gboolean ret = FALSE;
int armor = gpgme_get_armor (gpgme_ctx);
gpgme_set_armor (gpgme_ctx, 1);
/* Now, use the API to import ASCII-armored keys */
if (self->key_ascii_files)
{
for (guint i = 0; i < self->key_ascii_files->len; i++)
{
gpgme_error_t gpg_error;
const char *path = self->key_ascii_files->pdata[i];
glnx_autofd int fd = -1;
g_auto(gpgme_data_t) kdata = NULL;
if (!glnx_openat_rdonly (AT_FDCWD, path, TRUE, &fd, error))
goto out;
gpg_error = gpgme_data_new_from_fd (&kdata, fd);
if (gpg_error != GPG_ERR_NO_ERROR)
{
ot_gpgme_throw (gpg_error, error, "Loading data from fd %i", fd);
goto out;
}
gpg_error = gpgme_op_import (gpgme_ctx, kdata);
if (gpg_error != GPG_ERR_NO_ERROR)
{
ot_gpgme_throw (gpg_error, error, "Failed to import key");
goto out;
}
}
}
ret = TRUE;
out:
gpgme_set_armor (gpgme_ctx, armor);
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,
@@ -106,8 +285,6 @@ _ostree_gpg_verifier_check_signature (OstreeGpgVerifier *self,
g_autoptr(GOutputStream) target_stream = NULL;
OstreeGpgVerifyResult *result = NULL;
gboolean success = FALSE;
GList *link;
int armor;
/* GPGME has no API for using multiple keyrings (aka, gpg --keyring),
* so we concatenate all the keyring files into one pubring.gpg in a
@@ -127,83 +304,10 @@ _ostree_gpg_verifier_check_signature (OstreeGpgVerifier *self,
cancellable, error))
goto out;
for (link = self->keyrings; link != NULL; link = link->next)
{
g_autoptr(GFileInputStream) source_stream = NULL;
GFile *keyring_file = link->data;
gssize bytes_written;
GError *local_error = NULL;
source_stream = g_file_read (keyring_file, cancellable, &local_error);
/* Disregard non-existent keyrings. */
if (g_error_matches (local_error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND))
{
g_clear_error (&local_error);
continue;
}
else if (local_error != NULL)
{
g_propagate_error (error, local_error);
goto out;
}
bytes_written = g_output_stream_splice (target_stream,
G_INPUT_STREAM (source_stream),
G_OUTPUT_STREAM_SPLICE_CLOSE_SOURCE,
cancellable, error);
if (bytes_written < 0)
goto out;
}
for (guint i = 0; i < self->keyring_data->len; i++)
{
GBytes *keyringd = self->keyring_data->pdata[i];
gsize len;
gsize bytes_written;
const guint8 *buf = g_bytes_get_data (keyringd, &len);
if (!g_output_stream_write_all (target_stream, buf, len, &bytes_written,
cancellable, error))
goto out;
}
if (!g_output_stream_close (target_stream, cancellable, error))
if (!_ostree_gpg_verifier_import_keys (self, result->context, target_stream,
cancellable, error))
goto out;
/* Save the previous armor value - we need it on for importing ASCII keys */
armor = gpgme_get_armor (result->context);
gpgme_set_armor (result->context, 1);
/* Now, use the API to import ASCII-armored keys */
if (self->key_ascii_files)
{
for (guint i = 0; i < self->key_ascii_files->len; i++)
{
const char *path = self->key_ascii_files->pdata[i];
glnx_autofd int fd = -1;
g_auto(gpgme_data_t) kdata = NULL;
if (!glnx_openat_rdonly (AT_FDCWD, path, TRUE, &fd, error))
goto out;
gpg_error = gpgme_data_new_from_fd (&kdata, fd);
if (gpg_error != GPG_ERR_NO_ERROR)
{
ot_gpgme_throw (gpg_error, error, "Loading data from fd %i", fd);
goto out;
}
gpg_error = gpgme_op_import (result->context, kdata);
if (gpg_error != GPG_ERR_NO_ERROR)
{
ot_gpgme_throw (gpg_error, error, "Failed to import key");
goto out;
}
}
}
gpgme_set_armor (result->context, armor);
/* Both the signed data and signature GBytes instances will outlive the
* gpgme_data_t structs, so we can safely reuse the GBytes memory buffer
* directly and avoid a copy. */
+6
View File
@@ -51,6 +51,12 @@ OstreeGpgVerifyResult *_ostree_gpg_verifier_check_signature (OstreeGpgVerifier *
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,
+11 -3
View File
@@ -372,9 +372,12 @@ create_file_copy_from_input_at (OstreeRepo *repo,
* checkout_file_hardlink().
*/
OstreeChecksumFlags flags = 0;
if (repo->disable_xattrs)
if (repo->disable_xattrs || repo->mode == OSTREE_REPO_MODE_BARE_USER_ONLY)
flags |= OSTREE_CHECKSUM_FLAGS_IGNORE_XATTRS;
if (repo->mode == OSTREE_REPO_MODE_BARE_USER_ONLY)
flags |= OSTREE_CHECKSUM_FLAGS_CANONICAL_PERMISSIONS;
g_autofree char *actual_checksum = NULL;
if (!ostree_checksum_file_at (destination_dfd, destination_name,
&dest_stbuf, OSTREE_OBJECT_TYPE_FILE,
@@ -478,6 +481,7 @@ checkout_file_hardlink (OstreeRepo *self,
}
else if (errno == EEXIST)
{
int saved_errno = errno;
/* When we get EEXIST, we need to handle the different overwrite modes. */
switch (options->overwrite_mode)
{
@@ -525,8 +529,11 @@ checkout_file_hardlink (OstreeRepo *self,
* shouldn't hit this anymore. https://github.com/ostreedev/ostree/pull/1258
* */
OstreeChecksumFlags flags = 0;
if (self->disable_xattrs)
flags |= OSTREE_CHECKSUM_FLAGS_IGNORE_XATTRS;
if (self->disable_xattrs || self->mode == OSTREE_REPO_MODE_BARE_USER_ONLY)
flags |= OSTREE_CHECKSUM_FLAGS_IGNORE_XATTRS;
if (self->mode == OSTREE_REPO_MODE_BARE_USER_ONLY)
flags |= OSTREE_CHECKSUM_FLAGS_CANONICAL_PERMISSIONS;
g_autofree char *actual_checksum = NULL;
if (!ostree_checksum_file_at (destination_dfd, destination_name,
@@ -560,6 +567,7 @@ checkout_file_hardlink (OstreeRepo *self,
else
{
g_assert_cmpint (options->overwrite_mode, ==, OSTREE_REPO_CHECKOUT_OVERWRITE_UNION_IDENTICAL);
errno = saved_errno;
return glnx_throw_errno_prefix (error, "Hardlinking %s to %s", loose_path, destination_name);
}
break;
+27 -11
View File
@@ -3117,7 +3117,7 @@ ostree_repo_write_commit_with_time (OstreeRepo *self,
* ostree_repo_read_commit_detached_metadata:
* @self: Repo
* @checksum: ASCII SHA256 commit checksum
* @out_metadata: (out) (transfer full): Metadata associated with commit in with format "a{sv}", or %NULL if none exists
* @out_metadata: (out) (nullable) (transfer full): Metadata associated with commit in with format "a{sv}", or %NULL if none exists
* @cancellable: Cancellable
* @error: Error
*
@@ -3132,6 +3132,8 @@ ostree_repo_read_commit_detached_metadata (OstreeRepo *self,
GCancellable *cancellable,
GError **error)
{
g_assert (out_metadata != NULL);
char buf[_OSTREE_LOOSE_PATH_MAX];
_ostree_loose_path (buf, checksum, OSTREE_OBJECT_TYPE_COMMIT_META, self->mode);
@@ -3286,22 +3288,35 @@ _ostree_repo_commit_modifier_apply (OstreeRepo *self,
GFileInfo *file_info,
GFileInfo **out_modified_info)
{
gboolean canonicalize_perms = FALSE;
gboolean has_filter = FALSE;
OstreeRepoCommitFilterResult result = OSTREE_REPO_COMMIT_FILTER_ALLOW;
GFileInfo *modified_info;
if (modifier == NULL ||
(modifier->filter == NULL &&
(modifier->flags & OSTREE_REPO_COMMIT_MODIFIER_FLAGS_CANONICAL_PERMISSIONS) == 0))
/* Auto-detect bare-user-only repo, force canonical permissions. */
if (self->mode == OSTREE_REPO_MODE_BARE_USER_ONLY)
canonicalize_perms = TRUE;
if (modifier != NULL)
{
if ((modifier->flags & OSTREE_REPO_COMMIT_MODIFIER_FLAGS_CANONICAL_PERMISSIONS) != 0)
canonicalize_perms = TRUE;
if (modifier->filter != NULL)
has_filter = TRUE;
}
if (!(canonicalize_perms || has_filter))
{
*out_modified_info = g_object_ref (file_info);
return OSTREE_REPO_COMMIT_FILTER_ALLOW;
return OSTREE_REPO_COMMIT_FILTER_ALLOW; /* Note: early return (no actions needed) */
}
modified_info = g_file_info_dup (file_info);
if (modifier->filter)
if (has_filter)
result = modifier->filter (self, path, modified_info, modifier->user_data);
if ((modifier->flags & OSTREE_REPO_COMMIT_MODIFIER_FLAGS_CANONICAL_PERMISSIONS) != 0)
if (canonicalize_perms)
{
guint mode = g_file_info_get_attribute_uint32 (modified_info, "unix::mode");
switch (g_file_info_get_file_type (file_info))
@@ -3369,8 +3384,9 @@ get_final_xattrs (OstreeRepo *self,
/* track whether the returned xattrs differ from the file on disk */
gboolean modified = TRUE;
const gboolean skip_xattrs = (modifier &&
modifier->flags & (OSTREE_REPO_COMMIT_MODIFIER_FLAGS_SKIP_XATTRS |
OSTREE_REPO_COMMIT_MODIFIER_FLAGS_CANONICAL_PERMISSIONS)) > 0;
(modifier->flags & (OSTREE_REPO_COMMIT_MODIFIER_FLAGS_SKIP_XATTRS |
OSTREE_REPO_COMMIT_MODIFIER_FLAGS_CANONICAL_PERMISSIONS)) > 0) ||
self->mode == OSTREE_REPO_MODE_BARE_USER_ONLY;
/* fetch on-disk xattrs if needed & not disabled */
g_autoptr(GVariant) original_xattrs = NULL;
@@ -3618,8 +3634,8 @@ write_content_to_mtree_internal (OstreeRepo *self,
/* Load flags into boolean constants for ease of readability (we also need to
* NULL-check modifier)
*/
const gboolean canonical_permissions = modifier &&
(modifier->flags & OSTREE_REPO_COMMIT_MODIFIER_FLAGS_CANONICAL_PERMISSIONS);
const gboolean canonical_permissions = self->mode == OSTREE_REPO_MODE_BARE_USER_ONLY ||
(modifier && (modifier->flags & OSTREE_REPO_COMMIT_MODIFIER_FLAGS_CANONICAL_PERMISSIONS));
const gboolean devino_canonical = modifier &&
(modifier->flags & OSTREE_REPO_COMMIT_MODIFIER_FLAGS_DEVINO_CANONICAL);
/* We currently only honor the CONSUME flag in the dfd_iter case to avoid even
+12 -3
View File
@@ -234,9 +234,17 @@ typedef OstreeRepo _OstreeRepoAutoTransaction;
static inline void
_ostree_repo_auto_transaction_cleanup (void *p)
{
if (p == NULL)
return;
g_return_if_fail (OSTREE_IS_REPO (p));
OstreeRepo *repo = p;
if (repo)
(void) ostree_repo_abort_transaction (repo, NULL, NULL);
g_autoptr(GError) error = NULL;
if (!ostree_repo_abort_transaction (repo, NULL, &error))
g_warning("Failed to auto-cleanup OSTree transaction: %s", error->message);
g_object_unref (repo);
}
static inline _OstreeRepoAutoTransaction *
@@ -246,7 +254,8 @@ _ostree_repo_auto_transaction_start (OstreeRepo *repo,
{
if (!ostree_repo_prepare_transaction (repo, NULL, cancellable, error))
return NULL;
return (_OstreeRepoAutoTransaction *)repo;
return (_OstreeRepoAutoTransaction *) g_object_ref (repo);
}
G_DEFINE_AUTOPTR_CLEANUP_FUNC (_OstreeRepoAutoTransaction, _ostree_repo_auto_transaction_cleanup)
+115
View File
@@ -270,6 +270,7 @@ _sign_verify_for_remote (GPtrArray *verifiers,
g_assert (out_success_message == NULL || *out_success_message == NULL);
g_assert (verifiers);
g_assert_cmpuint (verifiers->len, >=, 1);
for (guint i = 0; i < verifiers->len; i++)
{
@@ -346,6 +347,120 @@ _process_gpg_verify_result (OtPullData *pull_data,
}
#endif /* OSTREE_DISABLE_GPGME */
static gboolean
validate_metadata_size (const char *prefix, GBytes *buf, GError **error)
{
gsize len = g_bytes_get_size (buf);
if (len > OSTREE_MAX_METADATA_SIZE)
return glnx_throw (error, "%s is %" G_GUINT64_FORMAT " bytes, exceeding maximum %" G_GUINT64_FORMAT, prefix, (guint64)len, (guint64)OSTREE_MAX_METADATA_SIZE);
return TRUE;
}
/**
* ostree_repo_signature_verify_commit_data:
* @self: Repo
* @remote_name: Name of remote
* @commit_data: Commit object data (GVariant)
* @commit_metadata: Commit metadata (GVariant `a{sv}`), must contain at least one valid signature
* @flags: Optionally disable GPG or signapi
* @out_results: (nullable) (out) (transfer full): Textual description of results
* @error: Error
*
* Validate the commit data using the commit metadata which must
* contain at least one valid signature. If GPG and signapi are
* both enabled, then both must find at least one valid signature.
*/
gboolean
ostree_repo_signature_verify_commit_data (OstreeRepo *self,
const char *remote_name,
GBytes *commit_data,
GBytes *commit_metadata,
OstreeRepoVerifyFlags flags,
char **out_results,
GError **error)
{
g_assert (self);
g_assert (remote_name);
g_assert (commit_data);
gboolean gpg = !(flags & OSTREE_REPO_VERIFY_FLAGS_NO_GPG);
gboolean signapi = !(flags & OSTREE_REPO_VERIFY_FLAGS_NO_SIGNAPI);
// Must ask for at least one type of verification
if (!(gpg || signapi))
return glnx_throw (error, "No commit verification types enabled via API");
if (!validate_metadata_size ("Commit", commit_data, error))
return FALSE;
/* Nothing to check if detached metadata is absent */
if (commit_metadata == NULL)
return glnx_throw (error, "Can't verify commit without detached metadata");
if (!validate_metadata_size ("Commit metadata", commit_metadata, error))
return FALSE;
g_autoptr(GVariant) commit_metadata_v = g_variant_new_from_bytes (G_VARIANT_TYPE_VARDICT, commit_metadata, FALSE);
g_autoptr(GString) results_buf = g_string_new ("");
gboolean verified = FALSE;
if (gpg)
{
if (!ostree_repo_remote_get_gpg_verify (self, remote_name,
&gpg, error))
return FALSE;
}
/* TODO - we could cache this in the repo */
g_autoptr(GPtrArray) signapi_verifiers = NULL;
if (signapi)
{
if (!_signapi_init_for_remote (self, remote_name, &signapi_verifiers, NULL, error))
return FALSE;
}
if (!(gpg || signapi_verifiers))
return glnx_throw (error, "Cannot verify commit for remote %s; GPG verification disabled, and no signapi verifiers configured", remote_name);
#ifndef OSTREE_DISABLE_GPGME
if (gpg)
{
g_autoptr(OstreeGpgVerifyResult) result =
_ostree_repo_gpg_verify_with_metadata (self, commit_data,
commit_metadata_v,
remote_name,
NULL, NULL, NULL, error);
if (!result)
return FALSE;
if (!ostree_gpg_verify_result_require_valid_signature (result, error))
return FALSE;
const guint n_signatures = ostree_gpg_verify_result_count_all (result);
g_assert_cmpuint (n_signatures, >, 0);
for (guint jj = 0; jj < n_signatures; jj++)
{
ostree_gpg_verify_result_describe (result, jj, results_buf, "GPG: ",
OSTREE_GPG_SIGNATURE_FORMAT_DEFAULT);
}
verified = TRUE;
}
#endif /* OSTREE_DISABLE_GPGME */
if (signapi_verifiers)
{
g_autofree char *success_message = NULL;
if (!_sign_verify_for_remote (signapi_verifiers, commit_data, commit_metadata_v, &success_message, error))
return glnx_prefix_error (error, "Can't verify commit");
if (verified)
g_string_append_c (results_buf, '\n');
g_string_append (results_buf, success_message);
verified = TRUE;
}
/* Must be true since we did g_assert (gpg || signapi) */
g_assert (verified);
if (out_results)
*out_results = g_string_free (g_steal_pointer (&results_buf), FALSE);
return TRUE;
}
gboolean
_verify_unwritten_commit (OtPullData *pull_data,
const char *checksum,
+16 -1
View File
@@ -2439,7 +2439,7 @@ get_best_static_delta_start_for (OtPullData *pull_data,
if (!ostree_repo_load_commit (pull_data->repo, to_revision,
NULL, &to_rev_state, error))
return FALSE;
if (!(to_rev_state & OSTREE_REPO_COMMIT_STATE_PARTIAL))
if (!(commitstate_is_partial(pull_data, to_rev_state)))
{
/* We already have this commit, we're done! */
out_result->result = DELTA_SEARCH_RESULT_UNCHANGED;
@@ -3965,6 +3965,7 @@ ostree_repo_pull_with_options (OstreeRepo *self,
else
{
g_autofree char *unconfigured_state = NULL;
g_autofree char *custom_backend = NULL;
g_free (pull_data->remote_name);
pull_data->remote_name = g_strdup (remote_name_or_baseurl);
@@ -3996,6 +3997,20 @@ ostree_repo_pull_with_options (OstreeRepo *self,
"remote unconfigured-state: %s", unconfigured_state);
goto out;
}
if (!ostree_repo_get_remote_option (self, pull_data->remote_name,
"custom-backend", NULL,
&custom_backend,
error))
goto out;
if (custom_backend)
{
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
"Cannot fetch via libostree - remote '%s' uses custom backend '%s'",
pull_data->remote_name, custom_backend);
goto out;
}
}
if (pull_data->remote_name && !(disable_sign_verify && disable_sign_verify_summary))
+247 -81
View File
@@ -1589,7 +1589,6 @@ impl_repo_remote_add (OstreeRepo *self,
GError **error)
{
g_return_val_if_fail (name != NULL, FALSE);
g_return_val_if_fail (url != NULL, FALSE);
g_return_val_if_fail (options == NULL || g_variant_is_of_type (options, G_VARIANT_TYPE ("a{sv}")), FALSE);
if (!ostree_validate_remote_name (name, error))
@@ -1637,10 +1636,13 @@ impl_repo_remote_add (OstreeRepo *self,
remote->file = g_file_get_child (etc_ostree_remotes_d, basename);
}
if (g_str_has_prefix (url, "metalink="))
g_key_file_set_string (remote->options, remote->group, "metalink", url + strlen ("metalink="));
else
g_key_file_set_string (remote->options, remote->group, "url", url);
if (url)
{
if (g_str_has_prefix (url, "metalink="))
g_key_file_set_string (remote->options, remote->group, "metalink", url + strlen ("metalink="));
else
g_key_file_set_string (remote->options, remote->group, "url", url);
}
if (options)
keyfile_set_from_vardict (remote->options, remote->group, options);
@@ -1676,7 +1678,7 @@ impl_repo_remote_add (OstreeRepo *self,
* ostree_repo_remote_add:
* @self: Repo
* @name: Name of remote
* @url: URL for remote (if URL begins with metalink=, it will be used as such)
* @url: (allow-none): URL for remote (if URL begins with metalink=, it will be used as such)
* @options: (allow-none): GVariant of type a{sv}
* @cancellable: Cancellable
* @error: Error
@@ -1789,7 +1791,6 @@ impl_repo_remote_replace (OstreeRepo *self,
GError **error)
{
g_return_val_if_fail (name != NULL, FALSE);
g_return_val_if_fail (url != NULL, FALSE);
g_return_val_if_fail (options == NULL || g_variant_is_of_type (options, G_VARIANT_TYPE ("a{sv}")), FALSE);
if (!ostree_validate_remote_name (name, error))
@@ -1815,11 +1816,14 @@ impl_repo_remote_replace (OstreeRepo *self,
if (!g_key_file_remove_group (remote->options, remote->group, error))
return FALSE;
if (g_str_has_prefix (url, "metalink="))
g_key_file_set_string (remote->options, remote->group, "metalink",
url + strlen ("metalink="));
else
g_key_file_set_string (remote->options, remote->group, "url", url);
if (url)
{
if (g_str_has_prefix (url, "metalink="))
g_key_file_set_string (remote->options, remote->group, "metalink",
url + strlen ("metalink="));
else
g_key_file_set_string (remote->options, remote->group, "url", url);
}
if (options != NULL)
keyfile_set_from_vardict (remote->options, remote->group, options);
@@ -1866,7 +1870,7 @@ impl_repo_remote_replace (OstreeRepo *self,
* @sysroot: (allow-none): System root
* @changeop: Operation to perform
* @name: Name of remote
* @url: URL for remote (if URL begins with metalink=, it will be used as such)
* @url: (allow-none): URL for remote (if URL begins with metalink=, it will be used as such)
* @options: (allow-none): GVariant of type a{sv}
* @cancellable: Cancellable
* @error: Error
@@ -2353,6 +2357,144 @@ out:
#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 ("aa{sv}"));
g_auto(GVariantBuilder) uids_builder = OT_VARIANT_BUILDER_INITIALIZER;
g_variant_builder_init (&uids_builder, G_VARIANT_TYPE ("aa{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)
{
/* Get WKD update URLs if address set */
g_autofree char *advanced_url = NULL;
g_autofree char *direct_url = NULL;
if (uid->address != NULL)
{
if (!ot_gpg_wkd_urls (uid->address, &advanced_url, &direct_url,
error))
return FALSE;
}
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_dict_insert_value (&uid_dict, "advanced_url",
g_variant_new ("ms", advanced_url));
g_variant_dict_insert_value (&uid_dict, "direct_url",
g_variant_new ("ms", direct_url));
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 ("(@aa{sv}@aa{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
@@ -5338,6 +5480,89 @@ find_keyring (OstreeRepo *self,
return TRUE;
}
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)
{
g_autoptr(OstreeGpgVerifier) verifier = _ostree_gpg_verifier_new ();
if (remote_name == OSTREE_ALL_REMOTES)
{
/* Add all available remote keyring files. */
if (!_ostree_gpg_verifier_add_keyring_dir_at (verifier, self->repo_dir_fd, ".",
cancellable, error))
return FALSE;
}
else if (remote_name != NULL)
{
/* Add the remote's keyring file if it exists. */
g_autoptr(OstreeRemote) remote = NULL;
remote = _ostree_repo_get_remote_inherited (self, remote_name, error);
if (remote == NULL)
return FALSE;
g_autoptr(GBytes) keyring_data = NULL;
if (!find_keyring (self, remote, &keyring_data, cancellable, error))
return FALSE;
if (keyring_data != NULL)
{
_ostree_gpg_verifier_add_keyring_data (verifier, keyring_data, remote->keyring);
add_global_keyrings = FALSE;
}
g_auto(GStrv) gpgkeypath_list = NULL;
if (!ot_keyfile_get_string_list_with_separator_choice (remote->options,
remote->group,
"gpgkeypath",
";,",
&gpgkeypath_list,
error))
return FALSE;
if (gpgkeypath_list)
{
for (char **iter = gpgkeypath_list; *iter != NULL; ++iter)
if (!_ostree_gpg_verifier_add_keyfile_path (verifier, *iter,
cancellable, error))
return FALSE;
}
}
if (add_global_keyrings)
{
/* Use the deprecated global keyring directory. */
if (!_ostree_gpg_verifier_add_global_keyring_dir (verifier, cancellable, error))
return FALSE;
}
if (keyringdir)
{
if (!_ostree_gpg_verifier_add_keyring_dir (verifier, keyringdir,
cancellable, error))
return FALSE;
}
if (extra_keyring != NULL)
{
_ostree_gpg_verifier_add_keyring_file (verifier, extra_keyring);
}
if (out_verifier != NULL)
*out_verifier = g_steal_pointer (&verifier);
return TRUE;
}
static OstreeGpgVerifyResult *
_ostree_repo_gpg_verify_data_internal (OstreeRepo *self,
const gchar *remote_name,
@@ -5349,74 +5574,15 @@ _ostree_repo_gpg_verify_data_internal (OstreeRepo *self,
GError **error)
{
g_autoptr(OstreeGpgVerifier) verifier = NULL;
gboolean add_global_keyring_dir = TRUE;
verifier = _ostree_gpg_verifier_new ();
if (remote_name == OSTREE_ALL_REMOTES)
{
/* Add all available remote keyring files. */
if (!_ostree_gpg_verifier_add_keyring_dir_at (verifier, self->repo_dir_fd, ".",
cancellable, error))
return NULL;
}
else if (remote_name != NULL)
{
/* Add the remote's keyring file if it exists. */
g_autoptr(OstreeRemote) remote = NULL;
remote = _ostree_repo_get_remote_inherited (self, remote_name, error);
if (remote == NULL)
return NULL;
g_autoptr(GBytes) keyring_data = NULL;
if (!find_keyring (self, remote, &keyring_data, cancellable, error))
return NULL;
if (keyring_data != NULL)
{
_ostree_gpg_verifier_add_keyring_data (verifier, keyring_data, remote->keyring);
add_global_keyring_dir = FALSE;
}
g_auto(GStrv) gpgkeypath_list = NULL;
if (!ot_keyfile_get_string_list_with_separator_choice (remote->options,
remote->group,
"gpgkeypath",
";,",
&gpgkeypath_list,
error))
return NULL;
if (gpgkeypath_list)
{
for (char **iter = gpgkeypath_list; *iter != NULL; ++iter)
if (!_ostree_gpg_verifier_add_keyfile_path (verifier, *iter,
cancellable, error))
return NULL;
}
}
if (add_global_keyring_dir)
{
/* Use the deprecated global keyring directory. */
if (!_ostree_gpg_verifier_add_global_keyring_dir (verifier, cancellable, error))
return NULL;
}
if (keyringdir)
{
if (!_ostree_gpg_verifier_add_keyring_dir (verifier, keyringdir,
cancellable, error))
return NULL;
}
if (extra_keyring != NULL)
{
_ostree_gpg_verifier_add_keyring_file (verifier, extra_keyring);
}
if (!_ostree_repo_gpg_prepare_verifier (self,
remote_name,
keyringdir,
extra_keyring,
TRUE,
&verifier,
cancellable,
error))
return NULL;
return _ostree_gpg_verifier_check_signature (verifier,
data,
+70 -1
View File
@@ -678,10 +678,14 @@ typedef OstreeRepoCommitFilterResult (*OstreeRepoCommitFilter) (OstreeRepo *r
* @OSTREE_REPO_COMMIT_MODIFIER_FLAGS_NONE: No special flags
* @OSTREE_REPO_COMMIT_MODIFIER_FLAGS_SKIP_XATTRS: Do not process extended attributes
* @OSTREE_REPO_COMMIT_MODIFIER_FLAGS_GENERATE_SIZES: Generate size information.
* @OSTREE_REPO_COMMIT_MODIFIER_FLAGS_CANONICAL_PERMISSIONS: Canonicalize permissions for bare-user-only mode.
* @OSTREE_REPO_COMMIT_MODIFIER_FLAGS_CANONICAL_PERMISSIONS: Canonicalize permissions.
* @OSTREE_REPO_COMMIT_MODIFIER_FLAGS_ERROR_ON_UNLABELED: Emit an error if configured SELinux policy does not provide a label
* @OSTREE_REPO_COMMIT_MODIFIER_FLAGS_CONSUME: Delete added files/directories after commit; Since: 2017.13
* @OSTREE_REPO_COMMIT_MODIFIER_FLAGS_DEVINO_CANONICAL: If a devino cache hit is found, skip modifier filters (non-directories only); Since: 2017.14
*
* Flags modifying commit behavior. In bare-user-only mode, @OSTREE_REPO_COMMIT_MODIFIER_FLAGS_CANONICAL_PERMISSIONS
* and @OSTREE_REPO_COMMIT_MODIFIER_FLAGS_SKIP_XATTRS are automatically enabled.
*
*/
typedef enum {
OSTREE_REPO_COMMIT_MODIFIER_FLAGS_NONE = 0,
@@ -1425,6 +1429,48 @@ gboolean ostree_repo_remote_get_gpg_verify_summary (OstreeRepo *self,
const char *name,
gboolean *out_gpg_verify_summary,
GError **error);
/**
* OSTREE_GPG_KEY_GVARIANT_FORMAT:
*
* - aa{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
* - aa{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
* - key: `advanced_url`, value: `ms`, advanced WKD update URL
* - key: `direct_url`, value: `ms`, direct WKD update URL
* - a{sv} - Additional metadata dictionary. There are currently no
* additional metadata keys defined.
*
* Since: 2021.4
*/
#define OSTREE_GPG_KEY_GVARIANT_STRING "(aa{sv}aa{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,
@@ -1492,6 +1538,29 @@ OstreeGpgVerifyResult * ostree_repo_verify_summary (OstreeRepo *self,
GCancellable *cancellable,
GError **error);
/**
* OstreeRepoVerifyFlags:
* @OSTREE_REPO_VERIFY_FLAGS_NONE: No flags
* @OSTREE_REPO_VERIFY_FLAGS_NO_GPG: Skip GPG verification
* @OSTREE_REPO_VERIFY_FLAGS_NO_SIGNAPI: Skip all other signature verification methods
*
* Since: 2021.4
*/
typedef enum {
OSTREE_REPO_VERIFY_FLAGS_NONE = 0,
OSTREE_REPO_VERIFY_FLAGS_NO_GPG = (1 << 0),
OSTREE_REPO_VERIFY_FLAGS_NO_SIGNAPI = (1 << 1),
} OstreeRepoVerifyFlags;
_OSTREE_PUBLIC
gboolean ostree_repo_signature_verify_commit_data (OstreeRepo *self,
const char *remote_name,
GBytes *commit_data,
GBytes *commit_metadata,
OstreeRepoVerifyFlags flags,
char **out_results,
GError **error);
_OSTREE_PUBLIC
gboolean ostree_repo_regenerate_summary (OstreeRepo *self,
GVariant *additional_metadata,
+4 -3
View File
@@ -171,7 +171,8 @@ gboolean ostree_sign_dummy_data_verify (OstreeSign *self,
if (!g_variant_is_of_type (signatures, (GVariantType *) OSTREE_SIGN_METADATA_DUMMY_TYPE))
return glnx_throw (error, "signature: dummy: wrong type passed for verification");
for (gsize i = 0; i < g_variant_n_children(signatures); i++)
gsize n = g_variant_n_children(signatures);
for (gsize i = 0; i < n; i++)
{
g_autoptr (GVariant) child = g_variant_get_child_value (signatures, i);
g_autoptr (GBytes) signature = g_variant_get_data_as_bytes(child);
@@ -188,9 +189,9 @@ gboolean ostree_sign_dummy_data_verify (OstreeSign *self,
*out_success_message = g_strdup ("dummy: Signature verified");
return TRUE;
}
else
return glnx_throw (error, "signature: dummy: incorrect signature %" G_GSIZE_FORMAT, i);
}
if (n)
return glnx_throw (error, "signature: dummy: incorrect signatures found: %" G_GSIZE_FORMAT, n);
return glnx_throw (error, "signature: dummy: no signatures");
}
+3 -1
View File
@@ -646,7 +646,8 @@ ostree_sysroot_upgrader_deploy (OstreeSysrootUpgrader *self,
g_autoptr(OstreeDeployment) new_deployment = NULL;
/* Experimental flag to enable staging */
if (getenv ("OSTREE_EX_STAGE_DEPLOYMENTS"))
gboolean stage = (self->flags & OSTREE_SYSROOT_UPGRADER_FLAGS_STAGE) > 0 || getenv ("OSTREE_EX_STAGE_DEPLOYMENTS") != NULL;
if (stage)
{
if (!ostree_sysroot_stage_tree (self->sysroot, self->osname,
self->new_revision,
@@ -688,6 +689,7 @@ ostree_sysroot_upgrader_flags_get_type (void)
{
static const GFlagsValue values[] = {
{ OSTREE_SYSROOT_UPGRADER_FLAGS_IGNORE_UNCONFIGURED, "OSTREE_SYSROOT_UPGRADER_FLAGS_IGNORE_UNCONFIGURED", "ignore-unconfigured" },
{ OSTREE_SYSROOT_UPGRADER_FLAGS_STAGE, "OSTREE_SYSROOT_UPGRADER_FLAGS_STAGE", "stage" },
{ 0, NULL, NULL }
};
GType g_define_type_id =
+3
View File
@@ -35,12 +35,15 @@ G_BEGIN_DECLS
* OstreeSysrootUpgraderFlags:
* @OSTREE_SYSROOT_UPGRADER_FLAGS_NONE: No options
* @OSTREE_SYSROOT_UPGRADER_FLAGS_IGNORE_UNCONFIGURED: Do not error if the origin has an unconfigured-state key
* @OSTREE_SYSROOT_UPGRADER_FLAGS_STAGE: Enable "staging" (finalization at shutdown); recommended
* (Since: 2021.4)
*
* Flags controlling operation of an #OstreeSysrootUpgrader.
*/
typedef enum {
OSTREE_SYSROOT_UPGRADER_FLAGS_NONE = (1 << 0),
OSTREE_SYSROOT_UPGRADER_FLAGS_IGNORE_UNCONFIGURED = (1 << 1),
OSTREE_SYSROOT_UPGRADER_FLAGS_STAGE = (1 << 2),
} OstreeSysrootUpgraderFlags;
_OSTREE_PUBLIC
+1 -1
View File
@@ -1790,7 +1790,7 @@ ostree_sysroot_init_osname (OstreeSysroot *self,
return glnx_throw_errno_prefix (error, "fchmod %s", "var/tmp");
if (mkdirat (dfd, "var/lib", 0777) < 0)
return glnx_throw_errno_prefix (error, "Creating %s", "var/tmp");
return glnx_throw_errno_prefix (error, "Creating %s", "var/lib");
/* This needs to be available and properly labeled early during the boot
* process (before tmpfiles.d kicks in), so that journald can flush logs from
+4 -4
View File
@@ -43,7 +43,7 @@
*
* Since: 2017.4
*/
#define OSTREE_RELEASE_VERSION (3)
#define OSTREE_RELEASE_VERSION (4)
/**
* OSTREE_VERSION
@@ -52,7 +52,7 @@
*
* Since: 2017.4
*/
#define OSTREE_VERSION (2021.3)
#define OSTREE_VERSION (2021.4)
/**
* OSTREE_VERSION_S:
@@ -62,7 +62,7 @@
*
* Since: 2017.4
*/
#define OSTREE_VERSION_S "2021.3"
#define OSTREE_VERSION_S "2021.4"
#define OSTREE_ENCODE_VERSION(year,release) \
((year) << 16 | (release))
@@ -100,4 +100,4 @@
*
* Since: 2019.3
*/
#define OSTREE_BUILT_FEATURES "libsoup gpgme ex-fsverity libarchive selinux avahi libmount systemd release p2p"
#define OSTREE_BUILT_FEATURES "libsoup gpgme ex-fsverity libarchive selinux libmount systemd release p2p"
+75
View File
@@ -27,6 +27,7 @@
#include <gio/gunixoutputstream.h>
#include "libglnx.h"
#include "zbase32.h"
/* Like glnx_throw_errno_prefix, but takes @gpg_error */
gboolean
@@ -538,3 +539,77 @@ ot_gpgme_kill_agent (const char *homedir)
return;
}
}
/* Takes the SHA1 checksum of the local component of an email address and
* returns the zbase32 encoding.
*/
static char *
encode_wkd_local (const char *local)
{
g_return_val_if_fail (local != NULL, NULL);
guint8 digest[20] = { 0 };
gsize len = sizeof (digest);
g_autoptr(GChecksum) checksum = g_checksum_new (G_CHECKSUM_SHA1);
g_checksum_update (checksum, (const guchar *)local, -1);
g_checksum_get_digest (checksum, digest, &len);
char *encoded = zbase32_encode (digest, len);
/* If the returned string is NULL, then there must have been a memory
* allocation problem. Just exit immediately like g_malloc.
*/
if (encoded == NULL)
g_error ("%s: %s", G_STRLOC, g_strerror (errno));
return encoded;
}
/* Implementation of OpenPGP Web Key Directory URLs as defined in
* https://datatracker.ietf.org/doc/html/draft-koch-openpgp-webkey-service
*/
gboolean
ot_gpg_wkd_urls (const char *email,
char **out_advanced_url,
char **out_direct_url,
GError **error)
{
g_return_val_if_fail (email != NULL, FALSE);
g_auto(GStrv) email_parts = g_strsplit (email, "@", -1);
if (g_strv_length (email_parts) != 2)
{
g_set_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT,
"Invalid email address \"%s\"", email);
return FALSE;
}
g_autofree char *local_lowered = g_ascii_strdown (email_parts[0], -1);
g_autofree char *domain_lowered = g_ascii_strdown (email_parts[1], -1);
g_autofree char *local_encoded = encode_wkd_local (local_lowered);
g_autofree char *local_escaped = g_uri_escape_string (email_parts[0], NULL, FALSE);
g_autofree char *advanced_url = g_strdup_printf ("https://openpgpkey.%s"
"/.well-known/openpgpkey"
"/%s/hu/%s?l=%s",
email_parts[1],
domain_lowered,
local_encoded,
local_escaped);
g_debug ("GPG UID \"%s\" advanced WKD URL: %s", email, advanced_url);
g_autofree char *direct_url = g_strdup_printf ("https://%s"
"/.well-known/openpgpkey"
"/hu/%s?l=%s",
email_parts[1],
local_encoded,
local_escaped);
g_debug ("GPG UID \"%s\" direct WKD URL: %s", email, direct_url);
if (out_advanced_url != NULL)
*out_advanced_url = g_steal_pointer (&advanced_url);
if (out_direct_url != NULL)
*out_direct_url = g_steal_pointer (&direct_url);
return TRUE;
}
+5
View File
@@ -48,4 +48,9 @@ gpgme_ctx_t ot_gpgme_new_ctx (const char *homedir,
void ot_gpgme_kill_agent (const char *homedir);
gboolean ot_gpg_wkd_urls (const char *email,
char **out_advanced_url,
char **out_direct_url,
GError **error);
G_END_DECLS
+141
View File
@@ -0,0 +1,141 @@
/**
* copyright 2002, 2003 Bryce "Zooko" Wilcox-O'Hearn
* mailto:zooko@zooko.com
*
* See the end of this file for the free software, open source license (BSD-style).
*/
#include "zbase32.h"
#include <math.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h> /* XXX only for debug printfs */
static const char*const chars="ybndrfg8ejkmcpqxot1uwisza345h769";
/* Types from zstr */
/**
* A zstr is simply an unsigned int length and a pointer to a buffer of
* unsigned chars.
*/
typedef struct {
size_t len; /* the length of the string (not counting the null-terminating character) */
unsigned char* buf; /* pointer to the first byte */
} zstr;
/**
* A zstr is simply an unsigned int length and a pointer to a buffer of
* const unsigned chars.
*/
typedef struct {
size_t len; /* the length of the string (not counting the null-terminating character) */
const unsigned char* buf; /* pointer to the first byte */
} czstr;
/* Functions from zstr */
static zstr
new_z(const size_t len)
{
zstr result;
result.buf = (unsigned char *)malloc(len+1);
if (result.buf == NULL) {
result.len = 0;
return result;
}
result.len = len;
result.buf[len] = '\0';
return result;
}
/* Functions from zutil */
static size_t
divceil(size_t n, size_t d)
{
return n/d+((n%d)!=0);
}
static zstr b2a_l_extra_Duffy(const czstr os, const size_t lengthinbits)
{
zstr result = new_z(divceil(os.len*8, 5)); /* if lengthinbits is not a multiple of 8 then this is allocating space for 0, 1, or 2 extra quintets that will be truncated at the end of this function if they are not needed */
if (result.buf == NULL)
return result;
unsigned char* resp = result.buf + result.len; /* pointer into the result buffer, initially pointing to the "one-past-the-end" quintet */
const unsigned char* osp = os.buf + os.len; /* pointer into the os buffer, initially pointing to the "one-past-the-end" octet */
/* Now this is a real live Duff's device. You gotta love it. */
unsigned long x=0; /* to hold up to 32 bits worth of the input */
switch ((osp - os.buf) % 5) {
case 0:
do {
x = *--osp;
*--resp = chars[x % 32]; /* The least sig 5 bits go into the final quintet. */
x /= 32; /* ... now we have 3 bits worth in x... */
case 4:
x |= ((unsigned long)(*--osp)) << 3; /* ... now we have 11 bits worth in x... */
*--resp = chars[x % 32];
x /= 32; /* ... now we have 6 bits worth in x... */
*--resp = chars[x % 32];
x /= 32; /* ... now we have 1 bits worth in x... */
case 3:
x |= ((unsigned long)(*--osp)) << 1; /* The 8 bits from the 2-indexed octet. So now we have 9 bits worth in x... */
*--resp = chars[x % 32];
x /= 32; /* ... now we have 4 bits worth in x... */
case 2:
x |= ((unsigned long)(*--osp)) << 4; /* The 8 bits from the 1-indexed octet. So now we have 12 bits worth in x... */
*--resp = chars[x%32];
x /= 32; /* ... now we have 7 bits worth in x... */
*--resp = chars[x%32];
x /= 32; /* ... now we have 2 bits worth in x... */
case 1:
x |= ((unsigned long)(*--osp)) << 2; /* The 8 bits from the 0-indexed octet. So now we have 10 bits worth in x... */
*--resp = chars[x%32];
x /= 32; /* ... now we have 5 bits worth in x... */
*--resp = chars[x];
} while (osp > os.buf);
} /* switch ((osp - os.buf) % 5) */
/* truncate any unused trailing zero quintets */
result.len = divceil(lengthinbits, 5);
result.buf[result.len] = '\0';
return result;
}
static zstr b2a_l(const czstr os, const size_t lengthinbits)
{
return b2a_l_extra_Duffy(os, lengthinbits);
}
static zstr b2a(const czstr os)
{
return b2a_l(os, os.len*8);
}
char *
zbase32_encode(const unsigned char *data, size_t length)
{
czstr input = { length, data };
zstr output = b2a(input);
return (char *)output.buf;
}
/**
* Copyright (c) 2002 Bryce "Zooko" Wilcox-O'Hearn
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software to deal in this software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of this software, and to permit
* persons to whom this software is furnished to do so, subject to the following
* conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of this software.
*
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THIS SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THIS SOFTWARE.
*/
+49
View File
@@ -0,0 +1,49 @@
/**
* copyright 2002, 2003 Bryce "Zooko" Wilcox-O'Hearn
* mailto:zooko@zooko.com
*
* See the end of this file for the free software, open source license (BSD-style).
*/
#ifndef __INCL_base32_h
#define __INCL_base32_h
static char const* const base32_h_cvsid = "$Id: base32.h,v 1.11 2003/12/15 01:16:19 zooko Exp $";
static int const base32_vermaj = 0;
static int const base32_vermin = 9;
static int const base32_vermicro = 12;
static char const* const base32_vernum = "0.9.12";
#include <assert.h>
#include <stddef.h>
/**
* @param data to be zbase-32 encoded
* @param length size of the data buffer
*
* @return an allocated string containing the zbase-32 encoded representation
*/
char *zbase32_encode(const unsigned char *data, size_t length);
#endif /* #ifndef __INCL_base32_h */
/**
* Copyright (c) 2002 Bryce "Zooko" Wilcox-O'Hearn
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software to deal in this software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of this software, and to permit
* persons to whom this software is furnished to do so, subject to the following
* conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of this software.
*
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THIS SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THIS SOFTWARE.
*/
+35 -1
View File
@@ -31,7 +31,10 @@
#include <glib/gi18n.h>
static gboolean opt_verify;
static GOptionEntry options[] = {
{ "verify", 'V', 0, G_OPTION_ARG_NONE, &opt_verify, "Print the commit verification status", NULL },
{ NULL }
};
@@ -86,6 +89,12 @@ deployment_print_status (OstreeSysroot *sysroot,
g_autoptr(GVariant) commit_metadata = NULL;
if (commit)
commit_metadata = g_variant_get_child_value (commit, 0);
g_autoptr(GVariant) commit_detached_metadata = NULL;
if (commit)
{
if (!ostree_repo_read_commit_detached_metadata (repo, ref, &commit_detached_metadata, cancellable, error))
return FALSE;
}
const char *version = NULL;
const char *source_title = NULL;
@@ -139,7 +148,7 @@ deployment_print_status (OstreeSysroot *sysroot,
}
#ifndef OSTREE_DISABLE_GPGME
if (deployment_get_gpg_verify (deployment, repo))
if (!opt_verify && deployment_get_gpg_verify (deployment, repo))
{
g_autoptr(GString) output_buffer = g_string_sized_new (256);
/* Print any digital signatures on this commit. */
@@ -172,6 +181,31 @@ deployment_print_status (OstreeSysroot *sysroot,
g_print ("%s", output_buffer->str);
}
#endif /* OSTREE_DISABLE_GPGME */
if (opt_verify)
{
if (!commit)
return glnx_throw (error, "Cannot verify, failed to load commit");
if (origin == NULL)
return glnx_throw (error, "Cannot verify deployment with no origin");
g_autofree char *refspec = g_key_file_get_string (origin, "origin", "refspec", NULL);
if (refspec == NULL)
return glnx_throw (error, "No origin/refspec, cannot verify");
g_autofree char *remote = NULL;
if (!ostree_parse_refspec (refspec, &remote, NULL, NULL))
return FALSE;
if (remote == NULL)
return glnx_throw (error, "Cannot verify deployment without remote");
g_autoptr(GBytes) commit_data = g_variant_get_data_as_bytes (commit);
g_autoptr(GBytes) commit_detached_metadata_bytes =
commit_detached_metadata ? g_variant_get_data_as_bytes (commit_detached_metadata) : NULL;
g_autofree char *verify_text = NULL;
if (!ostree_repo_signature_verify_commit_data (repo, remote, commit_data, commit_detached_metadata_bytes, 0, &verify_text, error))
return FALSE;
g_print ("%s\n", verify_text);
}
return TRUE;
}
+8 -2
View File
@@ -37,6 +37,7 @@ static gboolean opt_reboot;
static gboolean opt_allow_downgrade;
static gboolean opt_pull_only;
static gboolean opt_deploy_only;
static gboolean opt_stage;
static char *opt_osname;
static char *opt_override_commit;
@@ -47,6 +48,7 @@ static GOptionEntry options[] = {
{ "override-commit", 0, 0, G_OPTION_ARG_STRING, &opt_override_commit, "Deploy CHECKSUM instead of the latest tree", "CHECKSUM" },
{ "pull-only", 0, 0, G_OPTION_ARG_NONE, &opt_pull_only, "Do not create a deployment, just download", NULL },
{ "deploy-only", 0, 0, G_OPTION_ARG_NONE, &opt_deploy_only, "Do not pull, only deploy", NULL },
{ "stage", 0, 0, G_OPTION_ARG_NONE, &opt_stage, "Enable staging (finalization at reboot time)", NULL },
{ NULL }
};
@@ -74,9 +76,13 @@ ot_admin_builtin_upgrade (int argc, char **argv, OstreeCommandInvocation *invoca
return FALSE;
}
OstreeSysrootUpgraderFlags flags = 0;
if (opt_stage)
flags |= OSTREE_SYSROOT_UPGRADER_FLAGS_STAGE;
g_autoptr(OstreeSysrootUpgrader) upgrader =
ostree_sysroot_upgrader_new_for_os (sysroot, opt_osname,
cancellable, error);
ostree_sysroot_upgrader_new_for_os_with_flags (sysroot, opt_osname, flags,
cancellable, error);
if (!upgrader)
return FALSE;
+21 -11
View File
@@ -432,7 +432,7 @@ ostree_builtin_commit (int argc, char **argv, OstreeCommandInvocation *invocatio
g_autoptr(GHashTable) skip_list = NULL;
OstreeRepoCommitModifierFlags flags = 0;
g_autoptr(OstreeSePolicy) policy = NULL;
OstreeRepoCommitModifier *modifier = NULL;
g_autoptr(OstreeRepoCommitModifier) modifier = NULL;
OstreeRepoTransactionStats stats;
struct CommitFilterData filter_data = { 0, };
g_autofree char *commit_body = NULL;
@@ -555,7 +555,26 @@ ostree_builtin_commit (int argc, char **argv, OstreeCommandInvocation *invocatio
detached_metadata = g_variant_ref_sink (g_variant_builder_end (builder));
}
if (opt_no_xattrs)
/* Check for conflicting options */
if (opt_canonical_permissions && opt_owner_uid > 0)
{
glnx_throw (error, "Cannot specify both --canonical-permissions and non-zero --owner-uid");
goto out;
}
if (opt_canonical_permissions && opt_owner_gid > 0)
{
glnx_throw (error, "Cannot specify both --canonical-permissions and non-zero --owner-gid");
goto out;
}
if (opt_selinux_policy && opt_selinux_policy_from_base)
{
glnx_throw (error, "Cannot specify both --selinux-policy and --selinux-policy-from-base");
goto out;
}
if (opt_canonical_permissions || repo->mode == OSTREE_REPO_MODE_BARE_USER_ONLY)
flags |= OSTREE_REPO_COMMIT_MODIFIER_FLAGS_CANONICAL_PERMISSIONS;
if (opt_no_xattrs || repo->mode == OSTREE_REPO_MODE_BARE_USER_ONLY)
flags |= OSTREE_REPO_COMMIT_MODIFIER_FLAGS_SKIP_XATTRS;
if (opt_consume)
flags |= OSTREE_REPO_COMMIT_MODIFIER_FLAGS_CONSUME;
@@ -564,17 +583,10 @@ ostree_builtin_commit (int argc, char **argv, OstreeCommandInvocation *invocatio
opt_link_checkout_speedup = TRUE; /* Imply this */
flags |= OSTREE_REPO_COMMIT_MODIFIER_FLAGS_DEVINO_CANONICAL;
}
if (opt_canonical_permissions)
flags |= OSTREE_REPO_COMMIT_MODIFIER_FLAGS_CANONICAL_PERMISSIONS;
if (opt_generate_sizes)
flags |= OSTREE_REPO_COMMIT_MODIFIER_FLAGS_GENERATE_SIZES;
if (opt_disable_fsync)
ostree_repo_set_disable_fsync (repo, TRUE);
if (opt_selinux_policy && opt_selinux_policy_from_base)
{
glnx_throw (error, "Cannot specify both --selinux-policy and --selinux-policy-from-base");
goto out;
}
if (flags != 0
|| opt_owner_uid >= 0
@@ -968,7 +980,5 @@ ostree_builtin_commit (int argc, char **argv, OstreeCommandInvocation *invocatio
out:
if (repo)
ostree_repo_abort_transaction (repo, cancellable, NULL);
if (modifier)
ostree_repo_commit_modifier_unref (modifier);
return ret;
}
+3
View File
@@ -44,6 +44,9 @@ static OstreeCommand remote_subcommands[] = {
{ "gpg-import", OSTREE_BUILTIN_FLAG_NONE,
ot_remote_builtin_gpg_import,
"Import GPG keys" },
{ "gpg-list-keys", OSTREE_BUILTIN_FLAG_NONE,
ot_remote_builtin_list_gpg_keys,
"Show remote GPG keys" },
#endif /* OSTREE_DISABLE_GPGME */
#ifdef HAVE_LIBCURL_OR_LIBSOUP
{ "add-cookie", OSTREE_BUILTIN_FLAG_NONE,
+120 -4
View File
@@ -53,6 +53,7 @@ ot_dump_variant (GVariant *variant)
static gchar *
format_timestamp (guint64 timestamp,
gboolean local_tz,
GError **error)
{
GDateTime *dt;
@@ -66,7 +67,19 @@ format_timestamp (guint64 timestamp,
return NULL;
}
str = g_date_time_format (dt, "%Y-%m-%d %H:%M:%S +0000");
if (local_tz)
{
/* Convert to local time and display in the locale's preferred
* representation.
*/
g_autoptr(GDateTime) dt_local = g_date_time_to_local (dt);
str = g_date_time_format (dt_local, "%c");
}
else
{
str = g_date_time_format (dt, "%Y-%m-%d %H:%M:%S +0000");
}
g_date_time_unref (dt);
return str;
@@ -124,7 +137,7 @@ dump_commit (GVariant *variant,
&subject, &body, &timestamp, NULL, NULL);
timestamp = GUINT64_FROM_BE (timestamp);
str = format_timestamp (timestamp, &local_error);
str = format_timestamp (timestamp, FALSE, &local_error);
if (!str)
{
g_assert (local_error); /* Pacify static analysis */
@@ -136,8 +149,8 @@ dump_commit (GVariant *variant,
g_print ("Parent: %s\n", parent);
}
g_autofree char *contents = ostree_commit_get_content_checksum (variant) ?: "<invalid commit>";
g_print ("ContentChecksum: %s\n", contents);
g_autofree char *contents = ostree_commit_get_content_checksum (variant);
g_print ("ContentChecksum: %s\n", contents ?: "<invalid commit>");
g_print ("Date: %s\n", str);
if ((version = ot_admin_checksum_version (variant)))
@@ -390,3 +403,106 @@ ot_dump_summary_bytes (GBytes *summary_bytes,
g_print ("%s: %s\n", key, value_str);
}
}
static gboolean
dump_gpg_subkey (GVariant *subkey,
gboolean primary,
GError **error)
{
const gchar *fingerprint = NULL;
gint64 created = 0;
gint64 expires = 0;
gboolean revoked = FALSE;
gboolean expired = FALSE;
gboolean invalid = FALSE;
(void) g_variant_lookup (subkey, "fingerprint", "&s", &fingerprint);
(void) g_variant_lookup (subkey, "created", "x", &created);
(void) g_variant_lookup (subkey, "expires", "x", &expires);
(void) g_variant_lookup (subkey, "revoked", "b", &revoked);
(void) g_variant_lookup (subkey, "expired", "b", &expired);
(void) g_variant_lookup (subkey, "invalid", "b", &invalid);
/* Convert timestamps from big endian if needed */
created = GINT64_FROM_BE (created);
expires = GINT64_FROM_BE (expires);
g_print ("%s: %s%s%s\n",
primary ? "Key" : " Subkey",
fingerprint,
revoked ? " (revoked)" : "",
invalid ? " (invalid)" : "");
g_autofree gchar *created_str = format_timestamp (created, TRUE,
error);
if (created_str == NULL)
return FALSE;
g_print ("%sCreated: %s\n",
primary ? " " : " ",
created_str);
if (expires > 0)
{
g_autofree gchar *expires_str = format_timestamp (expires, TRUE,
error);
if (expires_str == NULL)
return FALSE;
g_print ("%s%s: %s\n",
primary ? " " : " ",
expired ? "Expired" : "Expires",
expires_str);
}
return TRUE;
}
gboolean
ot_dump_gpg_key (GVariant *key,
GError **error)
{
if (!g_variant_is_of_type (key, OSTREE_GPG_KEY_GVARIANT_FORMAT))
return glnx_throw (error, "GPG key variant type doesn't match '%s'",
OSTREE_GPG_KEY_GVARIANT_STRING);
g_autoptr(GVariant) subkeys_v = g_variant_get_child_value (key, 0);
GVariantIter subkeys_iter;
g_variant_iter_init (&subkeys_iter, subkeys_v);
g_autoptr(GVariant) primary_key = NULL;
g_variant_iter_next (&subkeys_iter, "@a{sv}", &primary_key);
if (!dump_gpg_subkey (primary_key, TRUE, error))
return FALSE;
g_autoptr(GVariant) uids_v = g_variant_get_child_value (key, 1);
GVariantIter uids_iter;
g_variant_iter_init (&uids_iter, uids_v);
GVariant *uid_v = NULL;
while (g_variant_iter_loop (&uids_iter, "@a{sv}", &uid_v))
{
const gchar *uid = NULL;
gboolean revoked = FALSE;
gboolean invalid = FALSE;
(void) g_variant_lookup (uid_v, "uid", "&s", &uid);
(void) g_variant_lookup (uid_v, "revoked", "b", &revoked);
(void) g_variant_lookup (uid_v, "invalid", "b", &invalid);
g_print (" UID: %s%s%s\n",
uid,
revoked ? " (revoked)" : "",
invalid ? " (invalid)" : "");
const char *advanced_url = NULL;
const char *direct_url = NULL;
(void) g_variant_lookup (uid_v, "advanced_url", "m&s", &advanced_url);
(void) g_variant_lookup (uid_v, "direct_url", "m&s", &direct_url);
g_print (" Advanced update URL: %s\n", advanced_url ?: "");
g_print (" Direct update URL: %s\n", direct_url ?: "");
}
GVariant *subkey = NULL;
while (g_variant_iter_loop (&subkeys_iter, "@a{sv}", &subkey))
{
if (!dump_gpg_subkey (subkey, FALSE, error))
return FALSE;
}
return TRUE;
}
+3
View File
@@ -42,3 +42,6 @@ void ot_dump_object (OstreeObjectType objtype,
void ot_dump_summary_bytes (GBytes *summary_bytes,
OstreeDumpFlags flags);
gboolean ot_dump_gpg_key (GVariant *key,
GError **error);
+1 -2
View File
@@ -148,7 +148,6 @@ ostree_run (int argc,
OstreeCommand *commands,
GError **res_error)
{
OstreeCommand *command;
GError *error = NULL;
GCancellable *cancellable = NULL;
#ifndef BUILDOPT_TSAN
@@ -187,7 +186,7 @@ ostree_run (int argc,
argc = out;
command = commands;
OstreeCommand *command = commands;
while (command->name)
{
if (g_strcmp0 (command_name, command->name) == 0)
+24 -7
View File
@@ -35,6 +35,7 @@ static char *opt_gpg_import;
static char **opt_sign_verify;
static char *opt_contenturl;
static char *opt_collection_id;
static char *opt_custom_backend;
static char *opt_sysroot;
static char *opt_repo;
@@ -51,6 +52,7 @@ static GOptionEntry option_entries[] = {
{ "if-not-exists", 0, 0, G_OPTION_ARG_NONE, &opt_if_not_exists, "Do nothing if the provided remote exists", NULL },
{ "force", 0, 0, G_OPTION_ARG_NONE, &opt_force, "Replace the provided remote if it exists", NULL },
{ "gpg-import", 0, 0, G_OPTION_ARG_FILENAME, &opt_gpg_import, "Import GPG key from FILE", "FILE" },
{ "custom-backend", 0, 0, G_OPTION_ARG_STRING, &opt_custom_backend, "This remote has content not fetched via libostree", "NAME" },
{ "contenturl", 0, 0, G_OPTION_ARG_STRING, &opt_contenturl, "Use URL when fetching content", "URL" },
{ "collection-id", 0, 0, G_OPTION_ARG_STRING, &opt_collection_id,
"Globally unique ID for this repository as an collection of refs for redistribution to other repositories", "COLLECTION-ID" },
@@ -103,7 +105,7 @@ ot_remote_builtin_add (int argc, char **argv, OstreeCommandInvocation *invocatio
g_autoptr(OstreeRepo) repo = NULL;
g_autoptr(GString) sign_verify = NULL;
const char *remote_name;
const char *remote_url;
const char *remote_url = NULL;
g_autoptr(GVariantBuilder) optbuilder = NULL;
g_autoptr(GVariant) options = NULL;
gboolean ret = FALSE;
@@ -119,11 +121,26 @@ ot_remote_builtin_add (int argc, char **argv, OstreeCommandInvocation *invocatio
cancellable, error))
goto out;
if (argc < 3)
if (opt_custom_backend)
{
ot_util_usage_error (context, "NAME and URL must be specified", error);
goto out;
if (argc < 2)
{
ot_util_usage_error (context, "NAME must be specified", error);
goto out;
}
if (argc >= 3)
remote_url = argv[2];
}
else
{
if (argc < 3)
{
ot_util_usage_error (context, "NAME and URL must be specified", error);
goto out;
}
remote_url = argv[2];
}
remote_name = argv[1];
if (opt_if_not_exists && opt_force)
{
@@ -133,9 +150,6 @@ ot_remote_builtin_add (int argc, char **argv, OstreeCommandInvocation *invocatio
goto out;
}
remote_name = argv[1];
remote_url = argv[2];
optbuilder = g_variant_builder_new (G_VARIANT_TYPE ("a{sv}"));
if (argc > 3)
@@ -159,6 +173,9 @@ ot_remote_builtin_add (int argc, char **argv, OstreeCommandInvocation *invocatio
if (opt_contenturl != NULL)
g_variant_builder_add (optbuilder, "{s@v}",
"contenturl", g_variant_new_variant (g_variant_new_string (opt_contenturl)));
if (opt_custom_backend != NULL)
g_variant_builder_add (optbuilder, "{s@v}",
"custom-backend", g_variant_new_variant (g_variant_new_string (opt_custom_backend)));
for (char **iter = opt_set; iter && *iter; iter++)
{
@@ -0,0 +1,66 @@
/*
* Copyright (C) 2015 Red Hat, Inc.
*
* SPDX-License-Identifier: LGPL-2.0+
*
* 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.
*/
#include "config.h"
#include "otutil.h"
#include "ot-main.h"
#include "ot-dump.h"
#include "ot-remote-builtins.h"
/* ATTENTION:
* Please remember to update the bash-completion script (bash/ostree) and
* man page (man/ostree-remote.xml) when changing the option list.
*/
static GOptionEntry option_entries[] = {
{ NULL }
};
gboolean
ot_remote_builtin_list_gpg_keys (int argc,
char **argv,
OstreeCommandInvocation *invocation,
GCancellable *cancellable,
GError **error)
{
g_autoptr(GOptionContext) context = g_option_context_new ("NAME");
g_autoptr(OstreeRepo) repo = NULL;
if (!ostree_option_context_parse (context, option_entries, &argc, &argv,
invocation, &repo, cancellable, error))
return FALSE;
const char *remote_name = (argc > 1) ? argv[1] : NULL;
g_autoptr(GPtrArray) keys = NULL;
if (!ostree_repo_remote_get_gpg_keys (repo, remote_name, NULL, &keys,
cancellable, error))
return FALSE;
for (guint i = 0; i < keys->len; i++)
{
if (!ot_dump_gpg_key (keys->pdata[i], error))
return FALSE;
}
return TRUE;
}
+1
View File
@@ -32,6 +32,7 @@ G_BEGIN_DECLS
BUILTINPROTO(add);
BUILTINPROTO(delete);
BUILTINPROTO(gpg_import);
BUILTINPROTO(list_gpg_keys);
BUILTINPROTO(list);
#ifdef HAVE_LIBCURL_OR_LIBSOUP
BUILTINPROTO(add_cookie);