Major update to SELinux handling

We use the new unified OSTree API (OstreeSePolicy) to perform
labeling, rather than having our own here.

Also create a new rpm-ostree-relabeling-helper that is run to label
any leftover files such as /etc/fstab that we create offline, and also
to relabel the entire disk.
This commit is contained in:
Colin Walters 2014-02-20 07:59:09 -05:00
parent 9be80f1775
commit 39a7c458ef
7 changed files with 289 additions and 257 deletions

View File

@ -17,6 +17,11 @@
privlib_SCRIPTS = src/rpmqa-sorted src/repoquery-sorted
privlib_PROGRAMS = rpm-ostree-relabeling-helper
rpm_ostree_relabeling_helper_SOURCES = src/rpm-ostree-relabeling-helper.c
rpm_ostree_relabeling_helper_CFLAGS = $(AM_CFLAGS) $(PKGDEP_RPMOSTREE_CFLAGS)
rpm_ostree_relabeling_helper_LDADD = $(AM_LDFLAGS) $(PKGDEP_RPMOSTREE_LIBS)
bin_PROGRAMS += rpm-ostree
rpm_ostree_SOURCES = src/rpm-ostree.c \

View File

@ -29,9 +29,9 @@ srpm: dist-snapshot
sed -e "s,^Version:.*,Version: $(GITREV_FOR_PKG)," $(srcdir)/packaging/$(PACKAGE).spec.in > $(PACKAGE).spec
$(srcdir)/packaging/rpmbuild-cwd -bs $(PACKAGE).spec
rpm: srpm
$(srcdir)/packaging/rpmbuild-cwd --rebuild $(PKG_VER)*.src.rpm
local-rpm: srpm
$(srcdir)/packaging/rpmbuild-cwd --rebuild packaging/$(PKG_VER)*.src.rpm
buildinstall: rpm
mock: srpm
sudo yum localinstall $(PKG_VER)*.src.rpm

View File

@ -32,7 +32,7 @@ LT_INIT([disable-static])
PKG_PROG_PKG_CONFIG
PKG_CHECK_MODULES(PKGDEP_GIO_UNIX, [gio-unix-2.0])
PKG_CHECK_MODULES(PKGDEP_RPMOSTREE, [gio-unix-2.0 json-glib-1.0 ostree-1 libgsystem libselinux])
PKG_CHECK_MODULES(PKGDEP_RPMOSTREE, [gio-unix-2.0 json-glib-1.0 ostree-1 libgsystem])
AC_PATH_PROG([XSLTPROC], [xsltproc])
GOBJECT_INTROSPECTION_REQUIRE([1.34.0])

View File

@ -172,6 +172,10 @@ function createDisk(diskpath, cancellable, params) {
if (bootPartitionOffset > 0)
gfHandle.mount("/dev/sda" + bootPartitionOffset, "/boot");
gfHandle.extlinux("/boot");
// It's understandable that extlinux wants the loader to be
// immutable...except that later breaks our ability to set SELinux
// security contexts on it.
gfHandle.set_e2attrs("/boot/ldlinux.sys", "i", new Guestfs.SetE2attrs({ clear: Guestfs.Tristate.TRUE }));
gfHandle.umount_all();
_installSyslinux(gfHandle, cancellable);
gfHandle.part_set_bootable("/dev/sda", 1, true);
@ -406,11 +410,26 @@ function pullDeploy(mntdir, srcrepo, osname, target, revision, originRepoUrl, ca
ProcUtil.runSync(adminCmd, cancellable,
{logInitiation: true, env: adminEnv});
let sysroot = OSTree.Sysroot.new(mntdir);
sysroot.load(null);
let deployments = sysroot.get_deployments();
let newDeployment = deployments[0];
let newDeploymentDirectory = sysroot.get_deployment_directory(newDeployment);
let defaultFstab = 'UUID=' + ROOT_UUID + ' / ext4 defaults 1 1\n\
UUID=' + BOOT_UUID + ' /boot ext4 defaults 1 2\n\
UUID=' + SWAP_UUID + ' swap swap defaults 0 0\n';
let fstabPath = ostreeOsdir.resolve_relative_path('current/etc/fstab');
let fstabPath = newDeploymentDirectory.resolve_relative_path('etc/fstab');
fstabPath.replace_contents(defaultFstab, null, false, Gio.FileCreateFlags.REPLACE_DESTINATION, cancellable);
print("Labeling deployment root");
let libdir = Gio.File.new_for_path(GLib.getenv('OSTBUILD_LIBDIR'));
ProcUtil.runSync([libdir.get_child('rpm-ostree-relabeling-helper').get_path(),
newDeploymentDirectory.get_path(),
newDeploymentDirectory.get_path(),
""],
cancellable,
{ logInitiation: true });
};
function requestSnapshotForTree(task, resultdir, refname, revision) {

View File

@ -19,6 +19,7 @@
const GLib = imports.gi.GLib;
const Gio = imports.gi.Gio;
const Lang = imports.lang;
const OSTree = imports.gi.OSTree;
const Format = imports.format;
const GSystem = imports.gi.GSystem;
@ -72,6 +73,18 @@ const TaskEnsureDiskCaches = new Lang.Class({
if (!addKernelArgs) addKernelArgs = [];
LibQA.pullDeploy(mntdir, this.repo, osname, ref, revision, originRepoUrl,
cancellable, { addKernelArgs: addKernelArgs });
print("Doing initial labeling");
let sysroot = OSTree.Sysroot.new(mntdir);
sysroot.load(null);
let deployments = sysroot.get_deployments();
let newDeployment = deployments[0];
let newDeploymentDirectory = sysroot.get_deployment_directory(newDeployment);
ProcUtil.runSync([this.libdir.get_child('rpm-ostree-relabeling-helper').get_path(),
newDeploymentDirectory.get_path(),
mntdir.get_path(),
""],
cancellable,
{ logInitiation: true });
} finally {
gfmnt.umount(cancellable);
}

View File

@ -0,0 +1,236 @@
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
*
* Copyright (C) 2014 Colin Walters <walters@verbum.org>
*
* This program 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 licence 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 <string.h>
#include <glib-unix.h>
#include <ostree.h>
#include "libgsystem.h"
#define OSTREE_GIO_FAST_QUERYINFO ("standard::name,standard::type,standard::size,standard::is-symlink,standard::symlink-target," \
"unix::device,unix::inode,unix::mode,unix::uid,unix::gid,unix::rdev")
static char *
ptrarray_path_join (GPtrArray *path)
{
GString *path_buf;
path_buf = g_string_new ("");
if (path->len == 0)
g_string_append_c (path_buf, '/');
else
{
guint i;
for (i = 0; i < path->len; i++)
{
const char *elt = path->pdata[i];
g_string_append_c (path_buf, '/');
g_string_append (path_buf, elt);
}
}
return g_string_free (path_buf, FALSE);
}
static gboolean
relabel_one_path (OstreeSePolicy *sepolicy,
GFile *path,
GFileInfo *info,
GPtrArray *path_parts,
GCancellable *cancellable,
GError **error)
{
gboolean ret = FALSE;
gs_free char *relpath = NULL;
gs_free char *new_label = NULL;
relpath = ptrarray_path_join (path_parts);
if (!ostree_sepolicy_restorecon (sepolicy, relpath,
info, path,
OSTREE_SEPOLICY_RESTORECON_FLAGS_ALLOW_NOLABEL |
OSTREE_SEPOLICY_RESTORECON_FLAGS_KEEP_EXISTING,
&new_label,
cancellable, error))
{
g_prefix_error (error, "Setting context of %s: ", gs_file_get_path_cached (path));
goto out;
}
if (new_label)
g_print ("Set label of '%s' (as '%s') to '%s'\n",
gs_file_get_path_cached (path),
relpath,
new_label);
ret = TRUE;
out:
return ret;
}
static gboolean
relabel_recursively (OstreeSePolicy *sepolicy,
GFile *dir,
GFileInfo *dir_info,
GPtrArray *path_parts,
GCancellable *cancellable,
GError **error)
{
gboolean ret = FALSE;
gs_unref_object GFileEnumerator *direnum = NULL;
if (!relabel_one_path (sepolicy, dir, dir_info, path_parts,
cancellable, error))
goto out;
direnum = g_file_enumerate_children (dir, OSTREE_GIO_FAST_QUERYINFO,
G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
cancellable, error);
if (!direnum)
goto out;
while (TRUE)
{
GFileInfo *file_info;
GFile *child;
GFileType ftype;
if (!gs_file_enumerator_iterate (direnum, &file_info, &child,
cancellable, error))
goto out;
if (file_info == NULL)
break;
g_ptr_array_add (path_parts, (char*)gs_file_get_basename_cached (child));
ftype = g_file_info_get_file_type (file_info);
if (ftype == G_FILE_TYPE_DIRECTORY)
{
if (!relabel_recursively (sepolicy, child, file_info, path_parts,
cancellable, error))
goto out;
}
else
{
if (!relabel_one_path (sepolicy, child, file_info, path_parts,
cancellable, error))
goto out;
}
g_ptr_array_remove_index (path_parts, path_parts->len - 1);
}
ret = TRUE;
out:
return ret;
}
static gboolean
selinux_relabel_dir (OstreeSePolicy *sepolicy,
GFile *dir,
const char *prefix,
GCancellable *cancellable,
GError **error)
{
gboolean ret = FALSE;
gs_unref_ptrarray GPtrArray *path_parts = g_ptr_array_new ();
gs_unref_object GFileInfo *root_info = NULL;
root_info = g_file_query_info (dir, OSTREE_GIO_FAST_QUERYINFO,
G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
cancellable, error);
if (!root_info)
goto out;
g_ptr_array_add (path_parts, (char*)prefix);
if (!relabel_recursively (sepolicy, dir, root_info, path_parts,
cancellable, error))
{
g_prefix_error (error, "Relabeling /%s: ", prefix);
goto out;
}
ret = TRUE;
out:
return ret;
}
int
main (int argc,
char **argv)
{
GError *local_error = NULL;
GError **error = &local_error;
GCancellable *cancellable = NULL;
const char *policy_name;
gs_unref_object GFile *subpath = NULL;
const char *prefix;
gs_unref_object OstreeSePolicy *sepolicy = NULL;
gs_unref_object GFile *root = NULL;
if (argc <= 3)
{
g_printerr ("usage: %s ROOTFS SUBPATH PREFIX\n", argv[0]);
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
"Option processing failed");
goto out;
}
root = g_file_new_for_path (argv[1]);
subpath = g_file_new_for_path (argv[2]);
prefix = argv[3];
sepolicy = ostree_sepolicy_new (root, cancellable, error);
if (!sepolicy)
goto out;
policy_name = ostree_sepolicy_get_name (sepolicy);
if (policy_name)
{
g_print ("Relabeling using policy '%s'\n", policy_name);
if (!selinux_relabel_dir (sepolicy, subpath, prefix,
cancellable, error))
goto out;
}
else
g_print ("No SELinux policy found in root '%s'\n",
gs_file_get_path_cached (root));
out:
if (local_error != NULL)
{
int is_tty = isatty (1);
const char *prefix = "";
const char *suffix = "";
if (is_tty)
{
prefix = "\x1b[31m\x1b[1m"; /* red, bold */
suffix = "\x1b[22m\x1b[0m"; /* bold off, color reset */
}
g_printerr ("%serror: %s%s\n", prefix, suffix, local_error->message);
g_error_free (local_error);
return 2;
}
else
return 0;
}

View File

@ -27,8 +27,6 @@
#include <errno.h>
#include <unistd.h>
#include <stdlib.h>
#include <selinux/selinux.h>
#include <selinux/label.h>
#include "libgsystem.h"
static gboolean
@ -661,97 +659,6 @@ create_rootfs_from_yumroot_content (GFile *targetroot,
return ret;
}
static GVariant *
xattr_cb (OstreeRepo *repo,
const char *path,
GFileInfo *file_info,
gpointer user_data)
{
guint32 mode;
char *con;
int res;
GVariantBuilder builder;
GVariant *ret;
struct selabel_handle *hnd = user_data;
mode = g_file_info_get_attribute_uint32 (file_info, "unix::mode");
res = selabel_lookup_raw (hnd, &con, path, mode);
if (res != 0)
{
if (strcmp (path, "/") == 0)
{
g_printerr ("error: selabel_lookup_raw(/) failed: %s", strerror (errno));
exit (1);
}
else
g_printerr ("warning: selabel_lookup_raw(%s): %s\n", path, strerror (errno));
return NULL;
}
g_variant_builder_init (&builder, G_VARIANT_TYPE ("a(ayay)"));
g_variant_builder_add (&builder, "(@ay@ay)",
g_variant_new_bytestring ("security.selinux"),
g_variant_new_bytestring (con));
freecon (con);
ret = g_variant_builder_end (&builder);
g_variant_ref_sink (ret);
return ret;
}
static char *
selinux_type_from_config (GFile *config_path,
GCancellable *cancellable,
GError **error)
{
gboolean success = FALSE;
gs_unref_object GFileInputStream *filein = NULL;
gs_unref_object GDataInputStream *datain = NULL;
char *ret = NULL;
const char *selinuxtype_prefix = "SELINUXTYPE=";
filein = g_file_read (config_path, cancellable, error);
if (!filein)
goto out;
datain = g_data_input_stream_new ((GInputStream*)filein);
while (TRUE)
{
gsize len;
GError *temp_error = NULL;
gs_free char *line = g_data_input_stream_read_line_utf8 (datain, &len,
cancellable, &temp_error);
if (temp_error)
{
g_propagate_error (error, temp_error);
goto out;
}
if (!line)
break;
if (!g_str_has_prefix (line, selinuxtype_prefix))
continue;
ret = g_strstrip (g_strdup (line + strlen (selinuxtype_prefix)));
break;
}
success = TRUE;
out:
if (!success)
{
g_free (ret);
return NULL;
}
return ret;
}
gboolean
rpmostree_postprocess (GFile *rootfs,
GCancellable *cancellable,
@ -780,106 +687,6 @@ rpmostree_postprocess (GFile *rootfs,
return ret;
}
static gboolean
workaround_selinux_pcre_version_mismatch (GFile *dir,
GCancellable *cancellable,
GError **error)
{
gboolean ret = FALSE;
gs_unref_object GFile *contexts_files = g_file_resolve_relative_path (dir, "contexts/files");
gs_unref_object GFileEnumerator *direnum = NULL;
direnum = g_file_enumerate_children (contexts_files, "standard::name,standard::type",
G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
cancellable, error);
if (!direnum)
goto out;
while (TRUE)
{
GFileInfo *file_info;
GFile *child;
const char *name;
if (!gs_file_enumerator_iterate (direnum, &file_info, &child,
cancellable, error))
goto out;
if (!file_info)
break;
name = g_file_info_get_name (file_info);
if (g_file_info_get_file_type (file_info) == G_FILE_TYPE_REGULAR
&& g_str_has_suffix (name, ".bin"))
{
gs_free char *bintmp_name = g_strconcat (name, ".tmp", NULL);
gs_unref_object GFile *bin_tmp = g_file_get_child (contexts_files, bintmp_name);
g_print ("Saving '%s' as '%s'\n", gs_file_get_path_cached (child),
gs_file_get_path_cached (bin_tmp));
if (!gs_file_rename (child, bin_tmp, cancellable, error))
goto out;
}
}
ret = TRUE;
out:
return ret;
}
static gboolean
undo_workaround_selinux_pcre_version_mismatch (GFile *dir,
GCancellable *cancellable,
GError **error)
{
gboolean ret = FALSE;
gs_unref_object GFile *contexts_files = g_file_resolve_relative_path (dir, "contexts/files");
gs_unref_object GFileEnumerator *direnum = NULL;
direnum = g_file_enumerate_children (contexts_files, "standard::name,standard::type",
G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
cancellable, error);
if (!direnum)
goto out;
while (TRUE)
{
GFileInfo *file_info;
GFile *child;
const char *name;
if (!gs_file_enumerator_iterate (direnum, &file_info, &child,
cancellable, error))
goto out;
if (!file_info)
break;
name = g_file_info_get_name (file_info);
if (g_file_info_get_file_type (file_info) == G_FILE_TYPE_REGULAR
&& g_str_has_suffix (name, ".bin.tmp"))
{
const char *lastdot = strrchr (name, '.');
gs_free char *bin_name = NULL;
gs_unref_object GFile *bin_tmp = NULL;
g_assert (lastdot);
bin_name = g_strndup (name, lastdot - name);
bin_tmp = g_file_get_child (contexts_files, bin_name);
g_print ("Moving '%s' back to '%s'\n", gs_file_get_path_cached (child),
gs_file_get_path_cached (bin_tmp));
if (!gs_file_rename (child, bin_tmp, cancellable, error))
goto out;
}
}
ret = TRUE;
out:
return ret;
}
gboolean
rpmostree_commit (GFile *rootfs,
OstreeRepo *repo,
@ -895,52 +702,14 @@ rpmostree_commit (GFile *rootfs,
gs_free char *parent_revision = NULL;
gs_free char *new_revision = NULL;
gs_unref_object GFile *root_tree = NULL;
gs_unref_object GFile *selinux_root = NULL;
gs_unref_object OstreeSePolicy *sepolicy = NULL;
/* hardcode targeted policy for now */
if (enable_selinux)
{
gs_unref_object GFile *usr_etc_selinux = g_file_resolve_relative_path (rootfs, "usr/etc/selinux");
gs_unref_object GFile *usr_etc_selinux_config = g_file_resolve_relative_path (usr_etc_selinux, "config");
gs_free char *selinux_config_type = NULL;
if (g_file_query_exists (usr_etc_selinux_config, NULL))
{
selinux_config_type = selinux_type_from_config (usr_etc_selinux_config,
cancellable, error);
if (!selinux_config_type)
{
g_prefix_error (error, "Failed to read SELINUXTYPE= from '%s': ",
gs_file_get_path_cached (usr_etc_selinux_config));
goto out;
}
g_print ("Detected SELINUXTYPE=%s\n", selinux_config_type);
selinux_root = g_file_get_child (usr_etc_selinux, selinux_config_type);
}
if (selinux_root)
{
g_print ("Setting policy root: %s\n",
gs_file_get_path_cached (selinux_root));
if (selinux_set_policy_root (gs_file_get_path_cached (selinux_root)) != 0)
{
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
"selinux_set_policy_root(%s): %s",
gs_file_get_path_cached (selinux_root),
strerror (errno));
goto out;
}
g_print ("Enabling workaround for SELinux pcre versioning\n");
if (!workaround_selinux_pcre_version_mismatch (selinux_root, cancellable, error))
goto out;
}
else
{
g_print ("Note: SELinux enabled but no policy found in this tree\n");
enable_selinux = FALSE;
}
sepolicy = ostree_sepolicy_new (rootfs, cancellable, error);
if (!sepolicy)
goto out;
}
g_print ("Committing '%s' ...\n", gs_file_get_path_cached (rootfs));
@ -948,30 +717,20 @@ rpmostree_commit (GFile *rootfs,
goto out;
mtree = ostree_mutable_tree_new ();
commit_modifier = ostree_repo_commit_modifier_new (enable_selinux ? 0 : OSTREE_REPO_COMMIT_MODIFIER_FLAGS_SKIP_XATTRS, NULL, NULL, NULL);
if (enable_selinux)
/* For now skip ACLs */
commit_modifier = ostree_repo_commit_modifier_new (OSTREE_REPO_COMMIT_MODIFIER_FLAGS_SKIP_XATTRS, NULL, NULL, NULL);
if (sepolicy)
{
struct selabel_handle *hnd = selabel_open (SELABEL_CTX_FILE, NULL, 0);
if (!hnd)
{
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
"Failed selabel_open(): %s", strerror (errno));
goto out;
}
ostree_repo_commit_modifier_set_xattr_callback (commit_modifier, xattr_cb, NULL, hnd);
const char *policy_name = ostree_sepolicy_get_name (sepolicy);
g_print ("Labeling with SELinux policy '%s'\n", policy_name);
ostree_repo_commit_modifier_set_sepolicy (commit_modifier, sepolicy);
}
if (!ostree_repo_write_directory_to_mtree (repo, rootfs, mtree, commit_modifier, cancellable, error))
goto out;
if (!ostree_repo_write_mtree (repo, mtree, &root_tree, cancellable, error))
goto out;
if (enable_selinux)
{
g_print ("Undoing workaround for SELinux pcre versioning\n");
if (!undo_workaround_selinux_pcre_version_mismatch (selinux_root, cancellable, error))
goto out;
}
if (!ostree_repo_resolve_rev (repo, refname, TRUE, &parent_revision, error))
goto out;