New upstream version 2021.4
This commit is contained in:
commit
085ba80d7c
90 files changed
+5415
-3764
No files matched your search
@@ -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.
|
||||
*/
|
||||
@@ -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))
|
||||
{
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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. */
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
@@ -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 =
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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"
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
|
||||
@@ -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.
|
||||
*/
|
||||
@@ -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.
|
||||
*/
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
@@ -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, ×tamp, 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;
|
||||
}
|
||||
@@ -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);
|
||||
@@ -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)
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
Reference in new issue
Block a user