From 3337334be5c7abbb79796994523e445db53df838 Mon Sep 17 00:00:00 2001 From: Colin Walters Date: Wed, 19 Feb 2014 08:40:29 -0500 Subject: [PATCH] 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 | 369 ++++++--------------- src/libostree/ostree-types.h | 1 + 8 files changed, 721 insertions(+), 298 deletions(-) create mode 100644 src/libostree/ostree-sepolicy.c create mode 100644 src/libostree/ostree-sepolicy.h diff --git a/Makefile-libostree-defines.am b/Makefile-libostree-defines.am index 6b07d0e6..02037ed2 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 8e9aaca7..04d03be6 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 10d39718..1b3136de 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 @@ -1683,6 +1685,67 @@ apply_commit_filter (OstreeRepo *self, return _ostree_repo_commit_modifier_apply (self, modifier, relpath, file_info, out_modified_info); } +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, @@ -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 df7f3e0c..ad6533ae 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 00000000..8a0457a1 --- /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 + * + * 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 +#include +#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 00000000..19a067eb --- /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 + * + * 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 aa55db22..f5ee56f8 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 + * Copyright (C) 2012,2014 Colin Walters * * 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 */ #include "config.h" -#ifdef HAVE_SELINUX -#include -#include -#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)); - - if (!selinux_relabel_dir (sysroot, secontext, - deployment_var_path, "var", - 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; - - if (!selinux_relabel_file (secontext, deployment_var_labeled_tmp, "var", - cancellable, error)) - goto out; - - if (!gs_file_rename (deployment_var_labeled_tmp, deployment_var_labeled, - cancellable, error)) - goto out; + 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; + + 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; } 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 b15cbbc9..8be12584 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;