core: Change relabeling to use libostree's SELinux support
This function is now basically reimplementing all of the intelligence we have in libostree today. It wouldn't have worked before we fixed https://github.com/ostreedev/ostree/issues/1165 but now that's done, conceptually we just need to call `ostree_repo_commit_modifier_set_sepolicy()`. However, I had to drop the "number of files changed" since currently the libostree API doesn't support that. Also, in order to detect the case that content changes at all (so we still have some useful information in the journal), implement a "content hash" check. See also https://github.com/projectatomic/rpm-ostree/pull/1123 (And note we *don't* use `DEVINO_CANONICAL` here) Closes: #1138 Approved by: jlebon
This commit is contained in:
parent
0a347fefe8
commit
7c99809a71
@ -2394,145 +2394,13 @@ break_hardlinks_at (int dfd,
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static const char*
|
||||
get_selinux_label (GVariant *xattrs)
|
||||
{
|
||||
for (int i = 0; i < g_variant_n_children (xattrs); i++)
|
||||
{
|
||||
const char *name, *value;
|
||||
g_variant_get_child (xattrs, i, "(^&ay^&ay)", &name, &value);
|
||||
if (strcmp (name, "security.selinux") == 0)
|
||||
return value;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static GVariant*
|
||||
set_selinux_label (GVariant *xattrs,
|
||||
const char *new_label)
|
||||
{
|
||||
GVariantBuilder builder;
|
||||
g_variant_builder_init (&builder, G_VARIANT_TYPE ("a(ayay)"));
|
||||
|
||||
/* copy all the other xattrs */
|
||||
for (int i = 0; i < g_variant_n_children (xattrs); i++)
|
||||
{
|
||||
const char *name;
|
||||
g_autoptr(GVariant) value = NULL;
|
||||
|
||||
g_variant_get_child (xattrs, i, "(^&ay@ay)", &name, &value);
|
||||
if (strcmp (name, "security.selinux") != 0)
|
||||
g_variant_builder_add (&builder, "(^ay@ay)", name, value);
|
||||
}
|
||||
|
||||
/* add the label if any */
|
||||
if (new_label != NULL)
|
||||
g_variant_builder_add (&builder, "(^ay^ay)", "security.selinux", new_label);
|
||||
|
||||
return g_variant_ref_sink (g_variant_builder_end (&builder));
|
||||
}
|
||||
|
||||
static gboolean
|
||||
relabel_dir_recurse_at (OstreeRepo *repo,
|
||||
int dfd,
|
||||
const char *path,
|
||||
const char *prefix,
|
||||
OstreeSePolicy *sepolicy,
|
||||
guint *inout_n_changed,
|
||||
GCancellable *cancellable,
|
||||
GError **error)
|
||||
{
|
||||
g_auto(GLnxDirFdIterator) dfd_iter = { FALSE, };
|
||||
if (!glnx_dirfd_iterator_init_at (dfd, path, FALSE, &dfd_iter, error))
|
||||
return FALSE;
|
||||
|
||||
while (TRUE)
|
||||
{
|
||||
struct dirent *dent = NULL;
|
||||
|
||||
if (!glnx_dirfd_iterator_next_dent_ensure_dtype (&dfd_iter, &dent,
|
||||
cancellable, error))
|
||||
return FALSE;
|
||||
if (dent == NULL)
|
||||
break;
|
||||
|
||||
if (dent->d_type != DT_DIR &&
|
||||
dent->d_type != DT_REG &&
|
||||
dent->d_type != DT_LNK)
|
||||
continue;
|
||||
|
||||
g_autoptr(GVariant) cur_xattrs = NULL;
|
||||
if (!glnx_dfd_name_get_all_xattrs (dfd_iter.fd, dent->d_name, &cur_xattrs,
|
||||
cancellable, error))
|
||||
return FALSE;
|
||||
|
||||
/* may return NULL */
|
||||
const char *cur_label = get_selinux_label (cur_xattrs);
|
||||
|
||||
/* build the new full path to use for label lookup (we can't just use
|
||||
* glnx_fdrel_abspath() since that will just give a new /proc/self/fd/$fd
|
||||
* on each recursion) */
|
||||
g_autofree char *fullpath = g_build_filename (prefix, dent->d_name, NULL);
|
||||
g_autofree char *new_label = NULL;
|
||||
{
|
||||
struct stat stbuf;
|
||||
|
||||
if (!glnx_fstatat (dfd_iter.fd, dent->d_name, &stbuf,
|
||||
AT_SYMLINK_NOFOLLOW, error))
|
||||
return FALSE;
|
||||
|
||||
/* may be NULL */
|
||||
if (!ostree_sepolicy_get_label (sepolicy, fullpath, stbuf.st_mode,
|
||||
&new_label, cancellable, error))
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (g_strcmp0 (cur_label, new_label) != 0)
|
||||
{
|
||||
if (dent->d_type != DT_DIR)
|
||||
if (!rpmostree_break_hardlink (dfd_iter.fd, dent->d_name, 0,
|
||||
cancellable, error))
|
||||
return FALSE;
|
||||
|
||||
g_autoptr(GVariant) new_xattrs = set_selinux_label (cur_xattrs, new_label);
|
||||
if (!glnx_dfd_name_set_all_xattrs (dfd_iter.fd, dent->d_name,
|
||||
new_xattrs, cancellable, error))
|
||||
return FALSE;
|
||||
|
||||
(*inout_n_changed)++;
|
||||
}
|
||||
|
||||
if (dent->d_type == DT_DIR)
|
||||
if (!relabel_dir_recurse_at (repo, dfd_iter.fd, dent->d_name, fullpath,
|
||||
sepolicy, inout_n_changed, cancellable, error))
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
relabel_rootfs (OstreeRepo *repo,
|
||||
int dfd,
|
||||
const char *path,
|
||||
OstreeSePolicy *sepolicy,
|
||||
guint *inout_n_changed,
|
||||
GCancellable *cancellable,
|
||||
GError **error)
|
||||
{
|
||||
/* NB: this does mean that / itself will not be labeled properly, but that
|
||||
* doesn't matter since it will always exist during overlay */
|
||||
return relabel_dir_recurse_at (repo, dfd, path, "/", sepolicy,
|
||||
inout_n_changed, cancellable, error);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
relabel_one_package (RpmOstreeContext *self,
|
||||
int tmpdir_dfd,
|
||||
OstreeRepo *repo,
|
||||
DnfPackage *pkg,
|
||||
OstreeSePolicy *sepolicy,
|
||||
guint *inout_n_changed,
|
||||
gboolean *changed,
|
||||
GCancellable *cancellable,
|
||||
GError **error)
|
||||
{
|
||||
@ -2545,6 +2413,10 @@ relabel_one_package (RpmOstreeContext *self,
|
||||
if (!ostree_repo_resolve_rev (repo, cachebranch, FALSE,
|
||||
&commit_csum, error))
|
||||
return FALSE;
|
||||
g_autoptr(GVariant) orig_commit = NULL;
|
||||
if (!ostree_repo_load_commit (repo, commit_csum, &orig_commit, NULL, error))
|
||||
return FALSE;
|
||||
g_autofree char *orig_content_checksum = rpmostree_commit_content_checksum (orig_commit);
|
||||
|
||||
/* checkout the pkg and relabel, breaking hardlinks */
|
||||
g_autoptr(OstreeRepoDevInoCache) cache = ostree_repo_devino_cache_new ();
|
||||
@ -2554,12 +2426,6 @@ relabel_one_package (RpmOstreeContext *self,
|
||||
cancellable, error))
|
||||
return FALSE;
|
||||
|
||||
/* This is where the magic happens. We traverse the tree and relabel stuff,
|
||||
* making sure to break hardlinks if needed. */
|
||||
if (!relabel_rootfs (repo, tmpdir_dfd, pkg_dirname, sepolicy, inout_n_changed,
|
||||
cancellable, error))
|
||||
return FALSE;
|
||||
|
||||
/* write to the tree */
|
||||
g_autoptr(GFile) root = NULL;
|
||||
{
|
||||
@ -2570,6 +2436,7 @@ relabel_one_package (RpmOstreeContext *self,
|
||||
NULL, NULL, NULL);
|
||||
|
||||
ostree_repo_commit_modifier_set_devino_cache (modifier, cache);
|
||||
ostree_repo_commit_modifier_set_sepolicy (modifier, self->sepolicy);
|
||||
|
||||
if (!ostree_repo_write_dfd_to_mtree (repo, tmpdir_dfd, pkg_dirname, mtree,
|
||||
modifier, cancellable, error))
|
||||
@ -2605,6 +2472,13 @@ relabel_one_package (RpmOstreeContext *self,
|
||||
cancellable, error))
|
||||
return FALSE;
|
||||
|
||||
g_autoptr(GVariant) new_commit = NULL;
|
||||
if (!ostree_repo_load_commit (repo, new_commit_csum, &new_commit, NULL, error))
|
||||
return FALSE;
|
||||
g_autofree char *new_content_checksum = rpmostree_commit_content_checksum (new_commit);
|
||||
|
||||
*changed = !g_str_equal (orig_content_checksum, new_content_checksum);
|
||||
|
||||
ostree_repo_transaction_set_ref (repo, NULL, cachebranch,
|
||||
new_commit_csum);
|
||||
}
|
||||
@ -2646,22 +2520,18 @@ rpmostree_context_relabel (RpmOstreeContext *self,
|
||||
&relabel_tmpdir, error))
|
||||
return FALSE;
|
||||
|
||||
guint n_changed_files = 0;
|
||||
guint n_changed_pkgs = 0;
|
||||
const guint n_to_relabel = self->pkgs_to_relabel->len;
|
||||
for (guint i = 0; i < n_to_relabel; i++)
|
||||
{
|
||||
DnfPackage *pkg = self->pkgs_to_relabel->pdata[i];
|
||||
guint pkg_n_changed = 0;
|
||||
gboolean pkg_changed = 0;
|
||||
if (!relabel_one_package (self, relabel_tmpdir.fd, ostreerepo,
|
||||
pkg, self->sepolicy,
|
||||
&pkg_n_changed, cancellable, error))
|
||||
&pkg_changed, cancellable, error))
|
||||
return FALSE;
|
||||
if (pkg_n_changed > 0)
|
||||
{
|
||||
n_changed_files += pkg_n_changed;
|
||||
n_changed_pkgs++;
|
||||
}
|
||||
if (pkg_changed)
|
||||
n_changed_pkgs++;
|
||||
dnf_state_assert_done (hifstate);
|
||||
}
|
||||
|
||||
@ -2673,10 +2543,8 @@ rpmostree_context_relabel (RpmOstreeContext *self,
|
||||
rpmostree_output_percent_progress_end ();
|
||||
|
||||
sd_journal_send ("MESSAGE_ID=" SD_ID128_FORMAT_STR, SD_ID128_FORMAT_VAL(RPMOSTREE_MESSAGE_SELINUX_RELABEL),
|
||||
"MESSAGE=Relabeled %u/%u pkgs, %u file%s changed", n_changed_pkgs,
|
||||
n_to_relabel, n_changed_files, _NS(n_changed_files),
|
||||
"MESSAGE=Relabeled %u/%u pkgs", n_changed_pkgs, n_to_relabel,
|
||||
"RELABELED_PKGS=%u/%u", n_changed_pkgs, n_to_relabel,
|
||||
"RELABELED_N_CHANGED_FILES=%u", n_changed_files,
|
||||
NULL);
|
||||
|
||||
return TRUE;
|
||||
|
@ -675,6 +675,34 @@ rpmostree_cache_branch_to_nevra (const char *cachebranch)
|
||||
return g_string_free (r, FALSE);
|
||||
}
|
||||
|
||||
/* Comptute SHA-256(dirtree checksum, dirmeta checksum) of a commit;
|
||||
* this identifies the checksum of its *contents*, independent of metadata
|
||||
* like the commit timestamp. https://github.com/ostreedev/ostree/issues/1315
|
||||
*/
|
||||
char *
|
||||
rpmostree_commit_content_checksum (GVariant *commit)
|
||||
{
|
||||
g_autoptr(GChecksum) hasher = g_checksum_new (G_CHECKSUM_SHA256);
|
||||
char checksum[OSTREE_SHA256_STRING_LEN+1];
|
||||
const guint8 *csum;
|
||||
|
||||
/* Hash content checksum */
|
||||
g_autoptr(GVariant) csum_bytes = NULL;
|
||||
g_variant_get_child (commit, 6, "@ay", &csum_bytes);
|
||||
csum = ostree_checksum_bytes_peek (csum_bytes);
|
||||
ostree_checksum_inplace_from_bytes (csum, checksum);
|
||||
g_checksum_update (hasher, (guint8*)checksum, OSTREE_SHA256_STRING_LEN);
|
||||
g_clear_pointer (&csum_bytes, (GDestroyNotify)g_variant_unref);
|
||||
|
||||
/* Hash meta checksum */
|
||||
g_variant_get_child (commit, 7, "@ay", &csum_bytes);
|
||||
csum = ostree_checksum_bytes_peek (csum_bytes);
|
||||
ostree_checksum_inplace_from_bytes (csum, checksum);
|
||||
g_checksum_update (hasher, (guint8*)checksum, OSTREE_SHA256_STRING_LEN);
|
||||
|
||||
return g_strdup (g_checksum_get_string (hasher));
|
||||
}
|
||||
|
||||
/* Implementation taken from https://git.gnome.org/browse/libgsystem/tree/src/gsystem-log.c */
|
||||
gboolean
|
||||
rpmostree_stdout_is_journal (void)
|
||||
|
@ -127,6 +127,9 @@ rpmostree_decompose_sha256_nevra (const char **nevra,
|
||||
char *
|
||||
rpmostree_cache_branch_to_nevra (const char *cachebranch);
|
||||
|
||||
char *
|
||||
rpmostree_commit_content_checksum (GVariant *commit);
|
||||
|
||||
gboolean
|
||||
rpmostree_break_hardlink (int dfd,
|
||||
const char *path,
|
||||
|
@ -89,7 +89,7 @@ root=$(vm_get_deployment_root 0)
|
||||
assert_actual_label $root/usr/bin/baz bin_t
|
||||
cursor=$(vm_get_journal_cursor)
|
||||
vm_rpmostree upgrade
|
||||
vm_assert_journal_has_content $cursor 'Relabeled 1/1 pkgs, 1 file changed'
|
||||
vm_assert_journal_has_content $cursor 'Relabeled 1/1 pkgs'
|
||||
root=$(vm_get_deployment_root 0)
|
||||
assert_actual_label $root/usr/bin/baz install_exec_t
|
||||
echo "ok relabel"
|
||||
|
Loading…
Reference in New Issue
Block a user