[ostree] libostree: Split off SELinux OstreeSePolicy class
- From: Colin Walters <walters src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [ostree] libostree: Split off SELinux OstreeSePolicy class
- Date: Thu, 20 Feb 2014 12:54:10 +0000 (UTC)
commit 3337334be5c7abbb79796994523e445db53df838
Author: Colin Walters <walters verbum org>
Date: Wed Feb 19 08:40:29 2014 -0500
libostree: Split off SELinux OstreeSePolicy class
It's better if this is independent from the OstreeSysroot; for
example, a policy is active in a given deployment root at once, not
for a sysroot globally.
We can also collect SELinux-related API in one place.
Unfortunately at the moment there can be only one instance of this
class per process.
Makefile-libostree-defines.am | 1 +
Makefile-libostree.am | 1 +
src/libostree/ostree-repo-commit.c | 121 +++++++--
src/libostree/ostree-repo.h | 4 +
src/libostree/ostree-sepolicy.c | 456 +++++++++++++++++++++++++++++++++
src/libostree/ostree-sepolicy.h | 66 +++++
src/libostree/ostree-sysroot-deploy.c | 363 +++++++--------------------
src/libostree/ostree-types.h | 1 +
8 files changed, 718 insertions(+), 295 deletions(-)
---
diff --git a/Makefile-libostree-defines.am b/Makefile-libostree-defines.am
index 6b07d0e..02037ed 100644
--- a/Makefile-libostree-defines.am
+++ b/Makefile-libostree-defines.am
@@ -27,6 +27,7 @@ libostree_public_headers = \
src/libostree/ostree-types.h \
src/libostree/ostree-repo-file.h \
src/libostree/ostree-diff.h \
+ src/libostree/ostree-sepolicy.h \
src/libostree/ostree-sysroot.h \
src/libostree/ostree-deployment.h \
src/libostree/ostree-bootconfig-parser.h \
diff --git a/Makefile-libostree.am b/Makefile-libostree.am
index 8e9aaca..04d03be 100644
--- a/Makefile-libostree.am
+++ b/Makefile-libostree.am
@@ -56,6 +56,7 @@ libostree_1_la_SOURCES = \
src/libostree/ostree-repo-file.c \
src/libostree/ostree-repo-file-enumerator.c \
src/libostree/ostree-repo-file-enumerator.h \
+ src/libostree/ostree-sepolicy.c \
src/libostree/ostree-sysroot-private.h \
src/libostree/ostree-sysroot.c \
src/libostree/ostree-sysroot-cleanup.c \
diff --git a/src/libostree/ostree-repo-commit.c b/src/libostree/ostree-repo-commit.c
index 10d3971..1b3136d 100644
--- a/src/libostree/ostree-repo-commit.c
+++ b/src/libostree/ostree-repo-commit.c
@@ -1618,6 +1618,8 @@ struct OstreeRepoCommitModifier {
OstreeRepoCommitModifierXattrCallback xattr_callback;
GDestroyNotify xattr_destroy;
gpointer xattr_user_data;
+
+ OstreeSePolicy *sepolicy;
};
OstreeRepoCommitFilterResult
@@ -1684,6 +1686,67 @@ apply_commit_filter (OstreeRepo *self,
}
static gboolean
+get_modified_xattrs (OstreeRepo *self,
+ OstreeRepoCommitModifier *modifier,
+ const char *relpath,
+ GFileInfo *file_info,
+ GFile *path,
+ GVariant **out_xattrs,
+ GCancellable *cancellable,
+ GError **error)
+{
+ gboolean ret = FALSE;
+ gs_unref_variant GVariant *ret_xattrs = NULL;
+
+ if (modifier && modifier->xattr_callback)
+ {
+ ret_xattrs = modifier->xattr_callback (self, relpath, file_info,
+ modifier->xattr_user_data);
+ }
+ else if (!(modifier && (modifier->flags & OSTREE_REPO_COMMIT_MODIFIER_FLAGS_SKIP_XATTRS) > 0))
+ {
+ if (!gs_file_get_all_xattrs (path, &ret_xattrs, cancellable, error))
+ goto out;
+ }
+
+ if (modifier->sepolicy)
+ {
+ gs_free char *label = NULL;
+
+ if (!ostree_sepolicy_get_label (modifier->sepolicy, relpath,
+ g_file_info_get_attribute_uint32 (file_info, "unix::mode"),
+ &label, cancellable, error))
+ goto out;
+
+ if (label)
+ {
+ GVariantBuilder *builder;
+
+ if (ret_xattrs)
+ builder = ot_util_variant_builder_from_variant (ret_xattrs,
+ G_VARIANT_TYPE ("a(ayay)"));
+ else
+ builder = g_variant_builder_new (G_VARIANT_TYPE ("a(ayay)"));
+
+ g_variant_builder_add_value (builder,
+ g_variant_new ("(@ay ay)",
+ g_variant_new_bytestring ("security.selinux"),
+ g_variant_new_bytestring (label)));
+ if (ret_xattrs)
+ g_variant_unref (ret_xattrs);
+
+ ret_xattrs = g_variant_builder_end (builder);
+ g_variant_ref_sink (ret_xattrs);
+ }
+ }
+
+ ret = TRUE;
+ gs_transfer_out_value (out_xattrs, &ret_xattrs);
+ out:
+ return ret;
+}
+
+static gboolean
write_directory_to_mtree_internal (OstreeRepo *self,
GFile *dir,
OstreeMutableTree *mtree,
@@ -1751,16 +1814,10 @@ write_directory_to_mtree_internal (OstreeRepo *self,
if (filter_result == OSTREE_REPO_COMMIT_FILTER_ALLOW)
{
g_debug ("Adding: %s", gs_file_get_path_cached (dir));
- if (modifier && modifier->xattr_callback)
- {
- xattrs = modifier->xattr_callback (self, relpath, child_info,
- modifier->xattr_user_data);
- }
- else if (!(modifier && (modifier->flags & OSTREE_REPO_COMMIT_MODIFIER_FLAGS_SKIP_XATTRS) > 0))
- {
- if (!gs_file_get_all_xattrs (dir, &xattrs, cancellable, error))
- goto out;
- }
+ if (!get_modified_xattrs (self, modifier, relpath, child_info, dir,
+ &xattrs,
+ cancellable, error))
+ goto out;
if (!_ostree_repo_write_directory_meta (self, modified_info, xattrs, &child_file_csum,
cancellable, error))
@@ -1872,17 +1929,11 @@ write_directory_to_mtree_internal (OstreeRepo *self,
goto out;
}
- if (modifier && modifier->xattr_callback)
- {
- xattrs = modifier->xattr_callback (self, child_relpath, child_info,
- modifier->xattr_user_data);
- }
- else if (!(modifier && (modifier->flags &
OSTREE_REPO_COMMIT_MODIFIER_FLAGS_SKIP_XATTRS) > 0))
- {
- g_clear_pointer (&xattrs, (GDestroyNotify) g_variant_unref);
- if (!gs_file_get_all_xattrs (child, &xattrs, cancellable, error))
- goto out;
- }
+ if (!get_modified_xattrs (self, modifier,
+ child_relpath, child_info, child,
+ &xattrs,
+ cancellable, error))
+ goto out;
if (!ostree_raw_file_to_content_stream (file_input,
modified_info, xattrs,
@@ -2082,6 +2133,8 @@ ostree_repo_commit_modifier_unref (OstreeRepoCommitModifier *modifier)
if (modifier->xattr_destroy)
modifier->xattr_destroy (modifier->xattr_user_data);
+ g_clear_object (&modifier->sepolicy);
+
g_free (modifier);
return;
}
@@ -2094,8 +2147,9 @@ ostree_repo_commit_modifier_unref (OstreeRepoCommitModifier *modifier)
* @user_data: Data for @callback:
*
* If set, this function should return extended attributes to use for
- * the given path. This is useful for things like SELinux, where a build
- * system can label the files as it's committing to the repository.
+ * the given path. This is useful for things like ACLs and SELinux,
+ * where a build system can label the files as it's committing to the
+ * repository.
*/
void
ostree_repo_commit_modifier_set_xattr_callback (OstreeRepoCommitModifier *modifier,
@@ -2108,6 +2162,27 @@ ostree_repo_commit_modifier_set_xattr_callback (OstreeRepoCommitModifier *modif
modifier->xattr_user_data = user_data;
}
+/**
+ * ostree_repo_commit_modifier_set_sepolicy:
+ * @modifier: An #OstreeRepoCommitModifier
+ * @sepolicy: (allow-none): Policy to use for labeling
+ *
+ * If @policy is non-%NULL, use it to look up labels to use for
+ * "security.selinux" extended attributes.
+ *
+ * Note that any policy specified this way operates in addition to any
+ * extended attributes provided via
+ * ostree_repo_commit_modifier_set_xattr_callback(). However if both
+ * specify a value for "security.selinux", then the one from the
+ * policy wins.
+ */
+void
+ostree_repo_commit_modifier_set_sepolicy (OstreeRepoCommitModifier *modifier,
+ OstreeSePolicy *sepolicy)
+{
+ g_clear_object (&modifier->sepolicy);
+ modifier->sepolicy = sepolicy ? g_object_ref (sepolicy) : NULL;
+}
G_DEFINE_BOXED_TYPE(OstreeRepoCommitModifier, ostree_repo_commit_modifier,
ostree_repo_commit_modifier_ref,
diff --git a/src/libostree/ostree-repo.h b/src/libostree/ostree-repo.h
index df7f3e0..ad6533a 100644
--- a/src/libostree/ostree-repo.h
+++ b/src/libostree/ostree-repo.h
@@ -25,6 +25,7 @@
#include "ostree-core.h"
#include "ostree-types.h"
#include "ostree-async-progress.h"
+#include "ostree-sepolicy.h"
G_BEGIN_DECLS
@@ -313,6 +314,9 @@ void ostree_repo_commit_modifier_set_xattr_callback (OstreeRepoCommitModifier
GDestroyNotify destroy,
gpointer user_data);
+void ostree_repo_commit_modifier_set_sepolicy (OstreeRepoCommitModifier *modifier,
+ OstreeSePolicy *sepolicy);
+
OstreeRepoCommitModifier *ostree_repo_commit_modifier_ref (OstreeRepoCommitModifier *modifier);
void ostree_repo_commit_modifier_unref (OstreeRepoCommitModifier *modifier);
diff --git a/src/libostree/ostree-sepolicy.c b/src/libostree/ostree-sepolicy.c
new file mode 100644
index 0000000..8a0457a
--- /dev/null
+++ b/src/libostree/ostree-sepolicy.c
@@ -0,0 +1,456 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
+ *
+ * Copyright (C) 2014 Colin Walters <walters verbum org>
+ *
+ * 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"
+
+#ifdef HAVE_SELINUX
+#include <selinux/selinux.h>
+#include <selinux/label.h>
+#endif
+
+#include "otutil.h"
+#include "libgsystem.h"
+
+#include "ostree-sepolicy.h"
+#include "ostree-bootloader-uboot.h"
+#include "ostree-bootloader-syslinux.h"
+
+/**
+ * SECTION:libostree-sepolicy
+ * @title: SELinux policy management
+ * @short_description: Read SELinux policy and manage filesystem labels
+ *
+ * A #OstreeSePolicy object can load the SELinux policy from a given
+ * root and perform labeling.
+ */
+struct OstreeSePolicy {
+ GObject parent;
+
+ GFile *path;
+
+#ifdef HAVE_SELINUX
+ GFile *selinux_policy_root;
+ struct selabel_handle *selinux_hnd;
+ char *selinux_policy_name;
+#endif
+};
+
+typedef struct {
+ GObjectClass parent_class;
+} OstreeSePolicyClass;
+
+static void initable_iface_init (GInitableIface *initable_iface);
+
+enum {
+ PROP_0,
+
+ PROP_PATH
+};
+
+G_DEFINE_TYPE_WITH_CODE (OstreeSePolicy, ostree_sepolicy, G_TYPE_OBJECT,
+ G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE, initable_iface_init))
+
+static void
+ostree_sepolicy_finalize (GObject *object)
+{
+ OstreeSePolicy *self = OSTREE_SEPOLICY (object);
+
+ g_clear_object (&self->path);
+ g_clear_object (&self->selinux_policy_root);
+ g_clear_pointer (&self->selinux_policy_name, g_free);
+#ifdef HAVE_SELINUX
+ if (self->selinux_hnd)
+ {
+ selabel_close (self->selinux_hnd);
+ self->selinux_hnd = NULL;
+ }
+#endif
+
+ G_OBJECT_CLASS (ostree_sepolicy_parent_class)->finalize (object);
+}
+
+static void
+ostree_sepolicy_set_property(GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ OstreeSePolicy *self = OSTREE_SEPOLICY (object);
+
+ switch (prop_id)
+ {
+ case PROP_PATH:
+ /* Canonicalize */
+ self->path = g_file_new_for_path (gs_file_get_path_cached (g_value_get_object (value)));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+ostree_sepolicy_get_property(GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ OstreeSePolicy *self = OSTREE_SEPOLICY (object);
+
+ switch (prop_id)
+ {
+ case PROP_PATH:
+ g_value_set_object (value, self->path);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+ostree_sepolicy_constructed (GObject *object)
+{
+ OstreeSePolicy *self = OSTREE_SEPOLICY (object);
+
+ g_assert (self->path != NULL);
+
+ G_OBJECT_CLASS (ostree_sepolicy_parent_class)->constructed (object);
+}
+
+static void
+ostree_sepolicy_class_init (OstreeSePolicyClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->constructed = ostree_sepolicy_constructed;
+ object_class->get_property = ostree_sepolicy_get_property;
+ object_class->set_property = ostree_sepolicy_set_property;
+ object_class->finalize = ostree_sepolicy_finalize;
+
+ g_object_class_install_property (object_class,
+ PROP_PATH,
+ g_param_spec_object ("path",
+ "",
+ "",
+ G_TYPE_FILE,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+}
+
+static gboolean
+initable_init (GInitable *initable,
+ GCancellable *cancellable,
+ GError **error)
+{
+#ifdef HAVE_SELINUX
+ gboolean ret = FALSE;
+ OstreeSePolicy *self = OSTREE_SEPOLICY (initable);
+ gs_unref_object GFile *etc_selinux_dir = NULL;
+ gs_unref_object GFile *policy_config_path = NULL;
+ gs_unref_object GFile *policy_root = NULL;
+ gs_unref_object GFileInputStream *filein = NULL;
+ gs_unref_object GDataInputStream *datain = NULL;
+ gboolean enabled = FALSE;
+ char *policytype = NULL;
+ const char *selinux_prefix = "SELINUX=";
+ const char *selinuxtype_prefix = "SELINUXTYPE=";
+
+ etc_selinux_dir = g_file_resolve_relative_path (self->path, "etc/selinux");
+ if (!g_file_query_exists (etc_selinux_dir, NULL))
+ {
+ g_object_unref (etc_selinux_dir);
+ etc_selinux_dir = g_file_resolve_relative_path (self->path, "usr/etc/selinux");
+ }
+ policy_config_path = g_file_get_child (etc_selinux_dir, "config");
+
+ if (g_file_query_exists (policy_config_path, NULL))
+ {
+ filein = g_file_read (policy_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))
+ {
+ policytype = g_strstrip (g_strdup (line + strlen (selinuxtype_prefix)));
+ policy_root = g_file_get_child (etc_selinux_dir, policytype);
+ }
+ else if (g_str_has_prefix (line, selinux_prefix))
+ {
+ const char *enabled_str = line + strlen (selinux_prefix);
+ if (g_ascii_strncasecmp (enabled_str, "enforcing", strlen ("enforcing")) == 0 ||
+ g_ascii_strncasecmp (enabled_str, "permissive", strlen ("permissive")) == 0)
+ enabled = TRUE;
+ }
+ }
+ }
+
+ if (enabled)
+ {
+ g_setenv ("LIBSELINUX_DISABLE_PCRE_PRECOMPILED", "1", FALSE);
+ if (selinux_set_policy_root (gs_file_get_path_cached (policy_root)) != 0)
+ {
+ g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+ "selinux_set_policy_root(%s): %s",
+ gs_file_get_path_cached (etc_selinux_dir),
+ strerror (errno));
+ goto out;
+ }
+
+ self->selinux_hnd = selabel_open (SELABEL_CTX_FILE, NULL, 0);
+ if (!self->selinux_hnd)
+ {
+ g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+ "With policy root '%s': selabel_open(SELABEL_CTX_FILE): %s",
+ gs_file_get_path_cached (etc_selinux_dir),
+ strerror (errno));
+ goto out;
+ }
+
+ {
+ char *con = NULL;
+ if (selabel_lookup_raw (self->selinux_hnd, &con, "/", 0755) != 0)
+ {
+ g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+ "With policy root '%s': Failed to look up context of /: %s",
+ gs_file_get_path_cached (etc_selinux_dir),
+ strerror (errno));
+ goto out;
+ }
+ freecon (con);
+ }
+
+ self->selinux_policy_name = g_strdup (policytype);
+ self->selinux_policy_root = g_object_ref (etc_selinux_dir);
+ }
+
+ ret = TRUE;
+ out:
+ return ret;
+#else
+ return TRUE;
+#endif
+}
+
+static void
+ostree_sepolicy_init (OstreeSePolicy *self)
+{
+}
+
+static void
+initable_iface_init (GInitableIface *initable_iface)
+{
+ initable_iface->init = initable_init;
+}
+
+/**
+ * ostree_sepolicy_new:
+ * @path: Path to a root directory
+ *
+ * Returns: (transfer full): An accessor object for SELinux policy in root located at @path
+ */
+OstreeSePolicy*
+ostree_sepolicy_new (GFile *path,
+ GCancellable *cancellable,
+ GError **error)
+{
+ return g_initable_new (OSTREE_TYPE_SEPOLICY, cancellable, error, "path", path, NULL);
+}
+
+/**
+ * ostree_sepolicy_get_path:
+ * @self:
+ *
+ * Returns: (transfer none): Path to rootfs
+ */
+GFile *
+ostree_sepolicy_get_path (OstreeSePolicy *self)
+{
+ return self->path;
+}
+
+const char *
+ostree_sepolicy_get_name (OstreeSePolicy *self)
+{
+#ifdef HAVE_SELINUX
+ return self->selinux_policy_name;
+#else
+ return NULL;
+#endif
+}
+
+/**
+ * ostree_sepolicy_get_label:
+ * @self: Self
+ * @relpath: Path
+ * @unix_mode: Unix mode
+ * @out_label: (allow-none) (out) (transfer full): Return location for security context
+ * @cancellable: Cancellable
+ * @error: Error
+ *
+ * Store in @out_label the security context for the given @relpath and
+ * mode @unix_mode. If the policy does not specify a label, %NULL
+ * will be returned.
+ */
+gboolean
+ostree_sepolicy_get_label (OstreeSePolicy *self,
+ const char *relpath,
+ guint32 unix_mode,
+ char **out_label,
+ GCancellable *cancellable,
+ GError **error)
+{
+#ifdef HAVE_SELINUX
+ gboolean ret = FALSE;
+ int res;
+ char *con = NULL;
+
+ if (self->selinux_hnd)
+ {
+ res = selabel_lookup_raw (self->selinux_hnd, &con, relpath, unix_mode);
+ if (res != 0)
+ {
+ int errsv = errno;
+ if (errsv != ENOENT)
+ {
+ ot_util_set_error_from_errno (error, errsv);
+ goto out;
+ }
+ }
+ else
+ {
+ /* Ensure we consistently allocate with g_malloc */
+ *out_label = g_strdup (con);
+ freecon (con);
+ }
+ }
+
+ ret = TRUE;
+ out:
+ return ret;
+#else
+ return TRUE;
+#endif
+}
+
+/**
+ * ostree_sepolicy_restorecon:
+ * @self: Self
+ * @path: Path string to use for policy lookup
+ * @info: (allow-none): File attributes
+ * @target: Physical path to target file
+ * @flags: Flags controlling behavior
+ * @out_new_label: (allow-none) (out): New label, or %NULL if unchanged
+ * @cancellable: Cancellable
+ * @error: Error
+ *
+ * Reset the security context of @target based on the SELinux policy.
+ */
+gboolean
+ostree_sepolicy_restorecon (OstreeSePolicy *self,
+ const char *path,
+ GFileInfo *info,
+ GFile *target,
+ OstreeSePolicyRestoreconFlags flags,
+ char **out_new_label,
+ GCancellable *cancellable,
+ GError **error)
+{
+#ifdef HAVE_SELINUX
+ gboolean ret = FALSE;
+ gs_unref_object GFileInfo *src_info = NULL;
+ gs_free char *label = NULL;
+ gboolean do_relabel = TRUE;
+
+ if (info != NULL)
+ src_info = g_object_ref (info);
+ else
+ {
+ src_info = g_file_query_info (target, "unix::mode",
+ G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
+ cancellable, error);
+ if (!src_info)
+ goto out;
+ }
+
+ if (flags & OSTREE_SEPOLICY_RESTORECON_FLAGS_KEEP_EXISTING)
+ {
+ char *existing_con = NULL;
+ if (lgetfilecon_raw (gs_file_get_path_cached (target), &existing_con) > 0
+ && existing_con)
+ {
+ do_relabel = FALSE;
+ freecon (existing_con);
+ }
+ }
+
+ if (do_relabel)
+ {
+ if (!ostree_sepolicy_get_label (self, path,
+ g_file_info_get_attribute_uint32 (src_info, "unix::mode"),
+ &label,
+ cancellable, error))
+ goto out;
+
+ if (!label)
+ {
+ if (!(flags & OSTREE_SEPOLICY_RESTORECON_FLAGS_ALLOW_NOLABEL))
+ {
+ g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+ "No label found for '%s'", path);
+ goto out;
+ }
+ }
+ else
+ {
+ int res = lsetfilecon (gs_file_get_path_cached (target), label);
+ if (res != 0)
+ {
+ ot_util_set_error_from_errno (error, errno);
+ goto out;
+ }
+ }
+ }
+
+ ret = TRUE;
+ gs_transfer_out_value (out_new_label, &label);
+ out:
+ return ret;
+#else
+ return TRUE;
+#endif
+}
diff --git a/src/libostree/ostree-sepolicy.h b/src/libostree/ostree-sepolicy.h
new file mode 100644
index 0000000..19a067e
--- /dev/null
+++ b/src/libostree/ostree-sepolicy.h
@@ -0,0 +1,66 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
+ *
+ * Copyright (C) 2014 Colin Walters <walters verbum org>
+ *
+ * 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.
+ */
+
+#pragma once
+
+#include "ostree-types.h"
+
+G_BEGIN_DECLS
+
+#define OSTREE_TYPE_SEPOLICY ostree_sepolicy_get_type()
+#define OSTREE_SEPOLICY(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST ((obj), OSTREE_TYPE_SEPOLICY, OstreeSePolicy))
+#define OSTREE_IS_SEPOLICY(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), OSTREE_TYPE_SEPOLICY))
+
+GType ostree_sepolicy_get_type (void);
+
+OstreeSePolicy* ostree_sepolicy_new (GFile *path,
+ GCancellable *cancellable,
+ GError **error);
+
+GFile * ostree_sepolicy_get_path (OstreeSePolicy *self);
+
+const char *ostree_sepolicy_get_name (OstreeSePolicy *self);
+
+gboolean ostree_sepolicy_get_label (OstreeSePolicy *self,
+ const char *relpath,
+ guint32 unix_mode,
+ char **out_label,
+ GCancellable *cancellable,
+ GError **error);
+
+typedef enum {
+ OSTREE_SEPOLICY_RESTORECON_FLAGS_NONE,
+ OSTREE_SEPOLICY_RESTORECON_FLAGS_ALLOW_NOLABEL = (1 << 0),
+ OSTREE_SEPOLICY_RESTORECON_FLAGS_KEEP_EXISTING = (1 << 1)
+} OstreeSePolicyRestoreconFlags;
+
+gboolean ostree_sepolicy_restorecon (OstreeSePolicy *self,
+ const char *path,
+ GFileInfo *info,
+ GFile *target,
+ OstreeSePolicyRestoreconFlags flags,
+ char **out_new_label,
+ GCancellable *cancellable,
+ GError **error);
+
+G_END_DECLS
+
diff --git a/src/libostree/ostree-sysroot-deploy.c b/src/libostree/ostree-sysroot-deploy.c
index aa55db2..f5ee56f 100644
--- a/src/libostree/ostree-sysroot-deploy.c
+++ b/src/libostree/ostree-sysroot-deploy.c
@@ -1,6 +1,6 @@
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
*
- * Copyright (C) 2012 Colin Walters <walters verbum org>
+ * Copyright (C) 2012,2014 Colin Walters <walters verbum org>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
@@ -16,17 +16,10 @@
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
- *
- * Author: Colin Walters <walters verbum org>
*/
#include "config.h"
-#ifdef HAVE_SELINUX
-#include <selinux/selinux.h>
-#include <selinux/label.h>
-#endif
-
#include "ostree-sysroot-private.h"
#include "ostree-core-private.h"
#include "otutil.h"
@@ -243,74 +236,6 @@ checkout_deployment_tree (OstreeSysroot *sysroot,
return ret;
}
-#ifdef HAVE_SELINUX
-static gboolean
-get_selinux_policy_root (GFile *deployment_etc,
- GFile **out_policy_root,
- GCancellable *cancellable,
- GError **error)
-{
- gboolean ret = FALSE;
- gs_unref_object GFile *etc_selinux_dir = NULL;
- gs_unref_object GFile *policy_config_path = NULL;
- gs_unref_object GFile *ret_policy_root = NULL;
- gs_unref_object GFileInputStream *filein = NULL;
- gs_unref_object GDataInputStream *datain = NULL;
- gboolean enabled = FALSE;
- char *policytype = NULL;
- const char *selinux_prefix = "SELINUX=";
- const char *selinuxtype_prefix = "SELINUXTYPE=";
-
- etc_selinux_dir = g_file_get_child (deployment_etc, "selinux");
- policy_config_path = g_file_get_child (etc_selinux_dir, "config");
-
- if (g_file_query_exists (policy_config_path, NULL))
- {
- filein = g_file_read (policy_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))
- {
- policytype = g_strstrip (g_strdup (line + strlen (selinuxtype_prefix)));
- }
- else if (g_str_has_prefix (line, selinux_prefix))
- {
- const char *enabled_str = line + strlen (selinux_prefix);
- if (g_ascii_strncasecmp (enabled_str, "enforcing", strlen ("enforcing")) == 0 ||
- g_ascii_strncasecmp (enabled_str, "permissive", strlen ("permissive")) == 0)
- enabled = TRUE;
- }
- }
- }
-
- if (enabled)
- ret_policy_root = g_file_get_child (etc_selinux_dir, policytype);
-
- ret = TRUE;
- gs_transfer_out_value (out_policy_root, &ret_policy_root);
- out:
- return ret;
-}
-
static char *
ptrarray_path_join (GPtrArray *path)
{
@@ -336,69 +261,43 @@ ptrarray_path_join (GPtrArray *path)
}
static gboolean
-relabel_one_path (GFile *path,
+relabel_one_path (OstreeSysroot *sysroot,
+ OstreeSePolicy *sepolicy,
+ GFile *path,
GFileInfo *info,
GPtrArray *path_parts,
- struct selabel_handle *hnd,
GCancellable *cancellable,
GError **error)
{
gboolean ret = FALSE;
- guint32 mode;
gs_free char *relpath = NULL;
- char *con = NULL;
-
- mode = g_file_info_get_attribute_uint32 (info, "unix::mode");
relpath = ptrarray_path_join (path_parts);
-
- if (selabel_lookup_raw (hnd, &con, relpath, mode) != 0)
- {
- g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
- "selabel_lookup_raw(%s, %u): %s",
- relpath, mode, strerror (errno));
- goto out;
- }
-
- if (S_ISLNK (mode))
- {
- if (lsetfilecon (gs_file_get_path_cached (path), con) != 0)
- {
- g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
- "lsetfilecon(%s): %s",
- gs_file_get_path_cached (path), strerror (errno));
- goto out;
- }
- }
- else
- {
- if (setfilecon (gs_file_get_path_cached (path), con) != 0)
- {
- g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
- "setfilecon(%s): %s",
- gs_file_get_path_cached (path), strerror (errno));
- goto out;
- }
- }
+ if (!ostree_sepolicy_restorecon (sepolicy, relpath,
+ info, path,
+ OSTREE_SEPOLICY_RESTORECON_FLAGS_ALLOW_NOLABEL,
+ NULL,
+ cancellable, error))
+ goto out;
ret = TRUE;
out:
- if (con) freecon (con);
return ret;
}
static gboolean
-relabel_recursively (GFile *dir,
+relabel_recursively (OstreeSysroot *sysroot,
+ OstreeSePolicy *sepolicy,
+ GFile *dir,
GFileInfo *dir_info,
GPtrArray *path_parts,
- struct selabel_handle *hnd,
GCancellable *cancellable,
GError **error)
{
gboolean ret = FALSE;
gs_unref_object GFileEnumerator *direnum = NULL;
- if (!relabel_one_path (dir, dir_info, path_parts, hnd,
+ if (!relabel_one_path (sysroot, sepolicy, dir, dir_info, path_parts,
cancellable, error))
goto out;
@@ -425,13 +324,13 @@ relabel_recursively (GFile *dir,
ftype = g_file_info_get_file_type (file_info);
if (ftype == G_FILE_TYPE_DIRECTORY)
{
- if (!relabel_recursively (child, file_info, path_parts, hnd,
+ if (!relabel_recursively (sysroot, sepolicy, child, file_info, path_parts,
cancellable, error))
goto out;
}
else
{
- if (!relabel_one_path (child, file_info, path_parts, hnd,
+ if (!relabel_one_path (sysroot, sepolicy, child, file_info, path_parts,
cancellable, error))
goto out;
}
@@ -444,202 +343,116 @@ relabel_recursively (GFile *dir,
return ret;
}
-#endif
-
-typedef struct {
- gboolean have_policy;
-#ifdef HAVE_SELINUX
- struct selabel_handle *hnd;
-#endif
-} OstreeLabelingContext;
-
-static gboolean
-init_labeling_context (GFile *deployment_etc,
- OstreeLabelingContext *secontext,
- GCancellable *cancellable,
- GError **error)
-{
-#ifdef HAVE_SELINUX
- gboolean ret = FALSE;
- gs_unref_object GFile *policy_root = NULL;
-
- if (!get_selinux_policy_root (deployment_etc, &policy_root,
- cancellable, error))
- goto out;
-
- if (policy_root)
- {
- secontext->have_policy = TRUE;
-
- g_print ("ostadmin: Using SELinux policy '%s'\n", gs_file_get_basename_cached (policy_root));
-
- if (selinux_set_policy_root (gs_file_get_path_cached (policy_root)) != 0)
- {
- g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
- "selinux_set_policy_root(%s): %s",
- gs_file_get_path_cached (policy_root),
- strerror (errno));
- goto out;
- }
- secontext->hnd = selabel_open (SELABEL_CTX_FILE, NULL, 0);
- if (!secontext->hnd)
- {
- g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
- "selabel_open(SELABEL_CTX_FILE): %s",
- strerror (errno));
- goto out;
- }
- }
- else
- secontext->have_policy = FALSE;
-
- ret = TRUE;
- out:
- return ret;
-#else
- secontext->have_policy = FALSE;
- return TRUE;
-#endif
-}
-
-static void
-ostree_labeling_context_cleanup (OstreeLabelingContext *secontext)
-{
-#ifdef HAVE_SELINUX
- if (secontext->hnd)
- selabel_close (secontext->hnd);
-#endif
-}
-
static gboolean
selinux_relabel_dir (OstreeSysroot *sysroot,
- OstreeLabelingContext *secontext,
+ OstreeSePolicy *sepolicy,
GFile *dir,
const char *prefix,
GCancellable *cancellable,
GError **error)
{
-#ifdef HAVE_SELINUX
gboolean ret = FALSE;
gs_unref_ptrarray GPtrArray *path_parts = g_ptr_array_new ();
gs_unref_object GFileInfo *root_info = NULL;
- if (secontext->have_policy)
+ 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 (sysroot, sepolicy, dir, root_info, path_parts,
+ cancellable, error))
{
- 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 (dir, root_info, path_parts, secontext->hnd,
- cancellable, error))
- {
- g_prefix_error (error, "Relabeling /%s: ", prefix);
- goto out;
- }
+ g_prefix_error (error, "Relabeling /%s: ", prefix);
+ goto out;
}
ret = TRUE;
out:
return ret;
-#else
- return TRUE;
-#endif
}
-#ifdef HAVE_SELINUX
static gboolean
-selinux_relabel_file (OstreeLabelingContext *secontext,
+selinux_relabel_file (OstreeSysroot *sysroot,
+ OstreeSePolicy *sepolicy,
GFile *path,
const char *prefix,
GCancellable *cancellable,
GError **error)
{
gboolean ret = FALSE;
+ gs_unref_ptrarray GPtrArray *path_parts = g_ptr_array_new ();
+ gs_unref_object GFileInfo *file_info = g_file_query_info (path, OSTREE_GIO_FAST_QUERYINFO,
+ G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
+ cancellable, error);
+ if (!file_info)
+ goto out;
- if (secontext->have_policy)
+ g_ptr_array_add (path_parts, (char*)prefix);
+ g_ptr_array_add (path_parts, (char*)gs_file_get_basename_cached (path));
+ if (!relabel_one_path (sysroot, sepolicy, path, file_info, path_parts,
+ cancellable, error))
{
- gs_unref_ptrarray GPtrArray *path_parts = g_ptr_array_new ();
- gs_unref_object GFileInfo *file_info = g_file_query_info (path, OSTREE_GIO_FAST_QUERYINFO,
- G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
- cancellable, error);
- if (!file_info)
- goto out;
-
- g_ptr_array_add (path_parts, (char*)prefix);
- g_ptr_array_add (path_parts, (char*)gs_file_get_basename_cached (path));
- if (!relabel_one_path (path, file_info, path_parts, secontext->hnd,
- cancellable, error))
- {
- g_prefix_error (error, "Relabeling /%s/%s: ", prefix,
- gs_file_get_basename_cached (path));
- goto out;
- }
+ g_prefix_error (error, "Relabeling /%s/%s: ", prefix,
+ gs_file_get_basename_cached (path));
+ goto out;
}
ret = TRUE;
out:
return ret;
}
-#endif
static gboolean
selinux_relabel_var_if_needed (OstreeSysroot *sysroot,
- OstreeLabelingContext *secontext,
+ OstreeSePolicy *sepolicy,
GFile *deployment_var_path,
GCancellable *cancellable,
GError **error)
{
-#ifdef HAVE_SELINUX
gboolean ret = FALSE;
-
- if (secontext->have_policy)
- {
- /* This is a bit of a hack; we should change the code at some
- * point in the distant future to only create (and label) /var
- * when doing a deployment.
- */
- gs_unref_object GFile *deployment_var_labeled =
- g_file_get_child (deployment_var_path, ".ostree-selabeled");
- gs_unref_object GFile *deployment_var_labeled_tmp =
- g_file_get_child (deployment_var_path, ".ostree-selabeled.tmp");
+ /* This is a bit of a hack; we should change the code at some
+ * point in the distant future to only create (and label) /var
+ * when doing a deployment.
+ */
+ gs_unref_object GFile *deployment_var_labeled =
+ g_file_get_child (deployment_var_path, ".ostree-selabeled");
+ gs_unref_object GFile *deployment_var_labeled_tmp =
+ g_file_get_child (deployment_var_path, ".ostree-selabeled.tmp");
- if (!g_file_query_exists (deployment_var_labeled, NULL))
+ if (!g_file_query_exists (deployment_var_labeled, NULL))
+ {
+ g_print ("ostadmin: Didn't find '%s', relabeling /var\n",
+ gs_file_get_path_cached (deployment_var_labeled));
+
+ if (!selinux_relabel_dir (sysroot, sepolicy,
+ deployment_var_path, "var",
+ cancellable, error))
{
- g_print ("ostadmin: Didn't find '%s', relabeling /var\n",
- gs_file_get_path_cached (deployment_var_labeled));
+ g_prefix_error (error, "Relabeling /var: ");
+ goto out;
+ }
- if (!selinux_relabel_dir (sysroot, secontext,
- deployment_var_path, "var",
+ if (!g_file_replace_contents (deployment_var_labeled_tmp, "", 0, NULL, FALSE,
+ G_FILE_CREATE_REPLACE_DESTINATION, NULL,
cancellable, error))
- {
- g_prefix_error (error, "Relabeling /var: ");
- goto out;
- }
-
- if (!g_file_replace_contents (deployment_var_labeled_tmp, "", 0, NULL, FALSE,
- G_FILE_CREATE_REPLACE_DESTINATION, NULL,
- cancellable, error))
- goto out;
+ goto out;
- if (!selinux_relabel_file (secontext, deployment_var_labeled_tmp, "var",
- cancellable, error))
- goto out;
+ if (!selinux_relabel_file (sysroot, sepolicy,
+ deployment_var_labeled_tmp, "var",
+ cancellable, error))
+ goto out;
- if (!gs_file_rename (deployment_var_labeled_tmp, deployment_var_labeled,
- cancellable, error))
- goto out;
- }
+ if (!gs_file_rename (deployment_var_labeled_tmp, deployment_var_labeled,
+ cancellable, error))
+ goto out;
}
ret = TRUE;
out:
return ret;
-#else
- return TRUE;
-#endif
}
static gboolean
@@ -647,6 +460,7 @@ merge_configuration (OstreeSysroot *sysroot,
OstreeDeployment *previous_deployment,
OstreeDeployment *deployment,
GFile *deployment_path,
+ OstreeSePolicy **out_sepolicy,
GCancellable *cancellable,
GError **error)
{
@@ -655,6 +469,7 @@ merge_configuration (OstreeSysroot *sysroot,
gs_unref_object GFile *source_etc_pristine_path = NULL;
gs_unref_object GFile *deployment_usretc_path = NULL;
gs_unref_object GFile *deployment_etc_path = NULL;
+ gs_unref_object OstreeSePolicy *sepolicy = NULL;
gboolean etc_exists;
gboolean usretc_exists;
@@ -703,21 +518,26 @@ merge_configuration (OstreeSysroot *sysroot,
if (usretc_exists)
{
- __attribute__((cleanup(ostree_labeling_context_cleanup))) OstreeLabelingContext new_default_secontext
= { 0, };
-
/* TODO - set out labels as we copy files */
g_assert (!etc_exists);
if (!gs_shutil_cp_a (deployment_usretc_path, deployment_etc_path,
cancellable, error))
goto out;
- if (!init_labeling_context (deployment_etc_path, &new_default_secontext,
- cancellable, error))
+ /* Here, we initialize SELinux policy from the /usr/etc inside
+ * the root - this is before we've finalized the configuration
+ * merge into /etc. */
+ sepolicy = ostree_sepolicy_new (deployment_path, cancellable, error);
+ if (!sepolicy)
goto out;
- if (!selinux_relabel_dir (sysroot, &new_default_secontext, deployment_etc_path, "etc",
- cancellable, error))
- goto out;
+ if (ostree_sepolicy_get_name (sepolicy) != NULL)
+ {
+ g_print ("ostadmin: Using SELinux policy '%s'\n", ostree_sepolicy_get_name (sepolicy));
+ if (!selinux_relabel_dir (sysroot, sepolicy, deployment_etc_path, "etc",
+ cancellable, error))
+ goto out;
+ }
g_print ("ostadmin: Created %s\n", gs_file_get_path_cached (deployment_etc_path));
}
@@ -733,6 +553,7 @@ merge_configuration (OstreeSysroot *sysroot,
}
ret = TRUE;
+ gs_transfer_out_value (out_sepolicy, &sepolicy);
out:
return ret;
}
@@ -1507,7 +1328,6 @@ ostree_sysroot_deploy_tree (OstreeSysroot *self,
{
gboolean ret = FALSE;
gint new_deployserial;
- __attribute__((cleanup(ostree_labeling_context_cleanup))) OstreeLabelingContext secontext = { 0, };
gs_unref_object OstreeDeployment *new_deployment = NULL;
gs_unref_object OstreeDeployment *merge_deployment = NULL;
gs_unref_object OstreeRepo *repo = NULL;
@@ -1518,6 +1338,7 @@ ostree_sysroot_deploy_tree (OstreeSysroot *self,
gs_unref_object GFile *tree_kernel_path = NULL;
gs_unref_object GFile *tree_initramfs_path = NULL;
gs_unref_object GFile *new_deployment_path = NULL;
+ gs_unref_object OstreeSePolicy *sepolicy = NULL;
gs_free char *new_bootcsum = NULL;
gs_unref_object OstreeBootconfigParser *bootconfig = NULL;
@@ -1590,6 +1411,7 @@ ostree_sysroot_deploy_tree (OstreeSysroot *self,
if (!merge_configuration (self, merge_deployment, new_deployment,
new_deployment_path,
+ &sepolicy,
cancellable, error))
{
g_prefix_error (error, "During /etc merge: ");
@@ -1598,10 +1420,7 @@ ostree_sysroot_deploy_tree (OstreeSysroot *self,
deployment_etc = g_file_get_child (new_deployment_path, "etc");
- if (!init_labeling_context (deployment_etc, &secontext, cancellable, error))
- goto out;
-
- if (!selinux_relabel_var_if_needed (self, &secontext, deployment_var,
+ if (!selinux_relabel_var_if_needed (self, sepolicy, deployment_var,
cancellable, error))
goto out;
diff --git a/src/libostree/ostree-types.h b/src/libostree/ostree-types.h
index b15cbbc..8be1258 100644
--- a/src/libostree/ostree-types.h
+++ b/src/libostree/ostree-types.h
@@ -27,6 +27,7 @@
G_BEGIN_DECLS
typedef struct OstreeRepo OstreeRepo;
+typedef struct OstreeSePolicy OstreeSePolicy;
typedef struct OstreeSysroot OstreeSysroot;
typedef struct OstreeMutableTree OstreeMutableTree;
typedef struct OstreeRepoFile OstreeRepoFile;
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]