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.
This commit is contained in:
Colin Walters 2014-02-19 08:40:29 -05:00
parent cc49096044
commit 3337334be5
8 changed files with 721 additions and 298 deletions

View File

@ -27,6 +27,7 @@ libostree_public_headers = \
src/libostree/ostree-types.h \ src/libostree/ostree-types.h \
src/libostree/ostree-repo-file.h \ src/libostree/ostree-repo-file.h \
src/libostree/ostree-diff.h \ src/libostree/ostree-diff.h \
src/libostree/ostree-sepolicy.h \
src/libostree/ostree-sysroot.h \ src/libostree/ostree-sysroot.h \
src/libostree/ostree-deployment.h \ src/libostree/ostree-deployment.h \
src/libostree/ostree-bootconfig-parser.h \ src/libostree/ostree-bootconfig-parser.h \

View File

@ -56,6 +56,7 @@ libostree_1_la_SOURCES = \
src/libostree/ostree-repo-file.c \ src/libostree/ostree-repo-file.c \
src/libostree/ostree-repo-file-enumerator.c \ src/libostree/ostree-repo-file-enumerator.c \
src/libostree/ostree-repo-file-enumerator.h \ src/libostree/ostree-repo-file-enumerator.h \
src/libostree/ostree-sepolicy.c \
src/libostree/ostree-sysroot-private.h \ src/libostree/ostree-sysroot-private.h \
src/libostree/ostree-sysroot.c \ src/libostree/ostree-sysroot.c \
src/libostree/ostree-sysroot-cleanup.c \ src/libostree/ostree-sysroot-cleanup.c \

View File

@ -1618,6 +1618,8 @@ struct OstreeRepoCommitModifier {
OstreeRepoCommitModifierXattrCallback xattr_callback; OstreeRepoCommitModifierXattrCallback xattr_callback;
GDestroyNotify xattr_destroy; GDestroyNotify xattr_destroy;
gpointer xattr_user_data; gpointer xattr_user_data;
OstreeSePolicy *sepolicy;
}; };
OstreeRepoCommitFilterResult OstreeRepoCommitFilterResult
@ -1683,6 +1685,67 @@ apply_commit_filter (OstreeRepo *self,
return _ostree_repo_commit_modifier_apply (self, modifier, relpath, file_info, out_modified_info); 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 static gboolean
write_directory_to_mtree_internal (OstreeRepo *self, write_directory_to_mtree_internal (OstreeRepo *self,
GFile *dir, GFile *dir,
@ -1751,16 +1814,10 @@ write_directory_to_mtree_internal (OstreeRepo *self,
if (filter_result == OSTREE_REPO_COMMIT_FILTER_ALLOW) if (filter_result == OSTREE_REPO_COMMIT_FILTER_ALLOW)
{ {
g_debug ("Adding: %s", gs_file_get_path_cached (dir)); g_debug ("Adding: %s", gs_file_get_path_cached (dir));
if (modifier && modifier->xattr_callback) if (!get_modified_xattrs (self, modifier, relpath, child_info, dir,
{ &xattrs,
xattrs = modifier->xattr_callback (self, relpath, child_info, cancellable, error))
modifier->xattr_user_data); goto out;
}
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 (!_ostree_repo_write_directory_meta (self, modified_info, xattrs, &child_file_csum, if (!_ostree_repo_write_directory_meta (self, modified_info, xattrs, &child_file_csum,
cancellable, error)) cancellable, error))
@ -1872,17 +1929,11 @@ write_directory_to_mtree_internal (OstreeRepo *self,
goto out; goto out;
} }
if (modifier && modifier->xattr_callback) if (!get_modified_xattrs (self, modifier,
{ child_relpath, child_info, child,
xattrs = modifier->xattr_callback (self, child_relpath, child_info, &xattrs,
modifier->xattr_user_data); cancellable, error))
} goto out;
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 (!ostree_raw_file_to_content_stream (file_input, if (!ostree_raw_file_to_content_stream (file_input,
modified_info, xattrs, modified_info, xattrs,
@ -2082,6 +2133,8 @@ ostree_repo_commit_modifier_unref (OstreeRepoCommitModifier *modifier)
if (modifier->xattr_destroy) if (modifier->xattr_destroy)
modifier->xattr_destroy (modifier->xattr_user_data); modifier->xattr_destroy (modifier->xattr_user_data);
g_clear_object (&modifier->sepolicy);
g_free (modifier); g_free (modifier);
return; return;
} }
@ -2094,8 +2147,9 @@ ostree_repo_commit_modifier_unref (OstreeRepoCommitModifier *modifier)
* @user_data: Data for @callback: * @user_data: Data for @callback:
* *
* If set, this function should return extended attributes to use for * If set, this function should return extended attributes to use for
* the given path. This is useful for things like SELinux, where a build * the given path. This is useful for things like ACLs and SELinux,
* system can label the files as it's committing to the repository. * where a build system can label the files as it's committing to the
* repository.
*/ */
void void
ostree_repo_commit_modifier_set_xattr_callback (OstreeRepoCommitModifier *modifier, 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; 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, G_DEFINE_BOXED_TYPE(OstreeRepoCommitModifier, ostree_repo_commit_modifier,
ostree_repo_commit_modifier_ref, ostree_repo_commit_modifier_ref,

View File

@ -25,6 +25,7 @@
#include "ostree-core.h" #include "ostree-core.h"
#include "ostree-types.h" #include "ostree-types.h"
#include "ostree-async-progress.h" #include "ostree-async-progress.h"
#include "ostree-sepolicy.h"
G_BEGIN_DECLS G_BEGIN_DECLS
@ -313,6 +314,9 @@ void ostree_repo_commit_modifier_set_xattr_callback (OstreeRepoCommitModifier
GDestroyNotify destroy, GDestroyNotify destroy,
gpointer user_data); gpointer user_data);
void ostree_repo_commit_modifier_set_sepolicy (OstreeRepoCommitModifier *modifier,
OstreeSePolicy *sepolicy);
OstreeRepoCommitModifier *ostree_repo_commit_modifier_ref (OstreeRepoCommitModifier *modifier); OstreeRepoCommitModifier *ostree_repo_commit_modifier_ref (OstreeRepoCommitModifier *modifier);
void ostree_repo_commit_modifier_unref (OstreeRepoCommitModifier *modifier); void ostree_repo_commit_modifier_unref (OstreeRepoCommitModifier *modifier);

View File

@ -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
}

View File

@ -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

View File

@ -1,6 +1,6 @@
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- /* -*- 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 * This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public * 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 * License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA. * Boston, MA 02111-1307, USA.
*
* Author: Colin Walters <walters@verbum.org>
*/ */
#include "config.h" #include "config.h"
#ifdef HAVE_SELINUX
#include <selinux/selinux.h>
#include <selinux/label.h>
#endif
#include "ostree-sysroot-private.h" #include "ostree-sysroot-private.h"
#include "ostree-core-private.h" #include "ostree-core-private.h"
#include "otutil.h" #include "otutil.h"
@ -243,74 +236,6 @@ checkout_deployment_tree (OstreeSysroot *sysroot,
return ret; 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 * static char *
ptrarray_path_join (GPtrArray *path) ptrarray_path_join (GPtrArray *path)
{ {
@ -336,69 +261,43 @@ ptrarray_path_join (GPtrArray *path)
} }
static gboolean static gboolean
relabel_one_path (GFile *path, relabel_one_path (OstreeSysroot *sysroot,
OstreeSePolicy *sepolicy,
GFile *path,
GFileInfo *info, GFileInfo *info,
GPtrArray *path_parts, GPtrArray *path_parts,
struct selabel_handle *hnd,
GCancellable *cancellable, GCancellable *cancellable,
GError **error) GError **error)
{ {
gboolean ret = FALSE; gboolean ret = FALSE;
guint32 mode;
gs_free char *relpath = NULL; gs_free char *relpath = NULL;
char *con = NULL;
mode = g_file_info_get_attribute_uint32 (info, "unix::mode");
relpath = ptrarray_path_join (path_parts); relpath = ptrarray_path_join (path_parts);
if (!ostree_sepolicy_restorecon (sepolicy, relpath,
if (selabel_lookup_raw (hnd, &con, relpath, mode) != 0) info, path,
{ OSTREE_SEPOLICY_RESTORECON_FLAGS_ALLOW_NOLABEL,
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, NULL,
"selabel_lookup_raw(%s, %u): %s", cancellable, error))
relpath, mode, strerror (errno)); goto out;
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;
}
}
ret = TRUE; ret = TRUE;
out: out:
if (con) freecon (con);
return ret; return ret;
} }
static gboolean static gboolean
relabel_recursively (GFile *dir, relabel_recursively (OstreeSysroot *sysroot,
OstreeSePolicy *sepolicy,
GFile *dir,
GFileInfo *dir_info, GFileInfo *dir_info,
GPtrArray *path_parts, GPtrArray *path_parts,
struct selabel_handle *hnd,
GCancellable *cancellable, GCancellable *cancellable,
GError **error) GError **error)
{ {
gboolean ret = FALSE; gboolean ret = FALSE;
gs_unref_object GFileEnumerator *direnum = NULL; 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)) cancellable, error))
goto out; goto out;
@ -425,13 +324,13 @@ relabel_recursively (GFile *dir,
ftype = g_file_info_get_file_type (file_info); ftype = g_file_info_get_file_type (file_info);
if (ftype == G_FILE_TYPE_DIRECTORY) 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)) cancellable, error))
goto out; goto out;
} }
else else
{ {
if (!relabel_one_path (child, file_info, path_parts, hnd, if (!relabel_one_path (sysroot, sepolicy, child, file_info, path_parts,
cancellable, error)) cancellable, error))
goto out; goto out;
} }
@ -444,202 +343,116 @@ relabel_recursively (GFile *dir,
return ret; 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 static gboolean
selinux_relabel_dir (OstreeSysroot *sysroot, selinux_relabel_dir (OstreeSysroot *sysroot,
OstreeLabelingContext *secontext, OstreeSePolicy *sepolicy,
GFile *dir, GFile *dir,
const char *prefix, const char *prefix,
GCancellable *cancellable, GCancellable *cancellable,
GError **error) GError **error)
{ {
#ifdef HAVE_SELINUX
gboolean ret = FALSE; gboolean ret = FALSE;
gs_unref_ptrarray GPtrArray *path_parts = g_ptr_array_new (); gs_unref_ptrarray GPtrArray *path_parts = g_ptr_array_new ();
gs_unref_object GFileInfo *root_info = NULL; 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_prefix_error (error, "Relabeling /%s: ", prefix);
G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, goto out;
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;
}
} }
ret = TRUE; ret = TRUE;
out: out:
return ret; return ret;
#else
return TRUE;
#endif
} }
#ifdef HAVE_SELINUX
static gboolean static gboolean
selinux_relabel_file (OstreeLabelingContext *secontext, selinux_relabel_file (OstreeSysroot *sysroot,
OstreeSePolicy *sepolicy,
GFile *path, GFile *path,
const char *prefix, const char *prefix,
GCancellable *cancellable, GCancellable *cancellable,
GError **error) GError **error)
{ {
gboolean ret = FALSE; 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 (); g_prefix_error (error, "Relabeling /%s/%s: ", prefix,
gs_unref_object GFileInfo *file_info = g_file_query_info (path, OSTREE_GIO_FAST_QUERYINFO, gs_file_get_basename_cached (path));
G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, goto out;
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;
}
} }
ret = TRUE; ret = TRUE;
out: out:
return ret; return ret;
} }
#endif
static gboolean static gboolean
selinux_relabel_var_if_needed (OstreeSysroot *sysroot, selinux_relabel_var_if_needed (OstreeSysroot *sysroot,
OstreeLabelingContext *secontext, OstreeSePolicy *sepolicy,
GFile *deployment_var_path, GFile *deployment_var_path,
GCancellable *cancellable, GCancellable *cancellable,
GError **error) GError **error)
{ {
#ifdef HAVE_SELINUX
gboolean ret = FALSE; gboolean ret = FALSE;
/* This is a bit of a hack; we should change the code at some
if (secontext->have_policy) * point in the distant future to only create (and label) /var
{ * when doing a deployment.
/* 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 gs_unref_object GFile *deployment_var_labeled =
* when doing a deployment. g_file_get_child (deployment_var_path, ".ostree-selabeled");
*/ gs_unref_object GFile *deployment_var_labeled_tmp =
gs_unref_object GFile *deployment_var_labeled = g_file_get_child (deployment_var_path, ".ostree-selabeled.tmp");
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", g_prefix_error (error, "Relabeling /var: ");
gs_file_get_path_cached (deployment_var_labeled)); goto out;
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;
} }
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; ret = TRUE;
out: out:
return ret; return ret;
#else
return TRUE;
#endif
} }
static gboolean static gboolean
@ -647,6 +460,7 @@ merge_configuration (OstreeSysroot *sysroot,
OstreeDeployment *previous_deployment, OstreeDeployment *previous_deployment,
OstreeDeployment *deployment, OstreeDeployment *deployment,
GFile *deployment_path, GFile *deployment_path,
OstreeSePolicy **out_sepolicy,
GCancellable *cancellable, GCancellable *cancellable,
GError **error) GError **error)
{ {
@ -655,6 +469,7 @@ merge_configuration (OstreeSysroot *sysroot,
gs_unref_object GFile *source_etc_pristine_path = NULL; gs_unref_object GFile *source_etc_pristine_path = NULL;
gs_unref_object GFile *deployment_usretc_path = NULL; gs_unref_object GFile *deployment_usretc_path = NULL;
gs_unref_object GFile *deployment_etc_path = NULL; gs_unref_object GFile *deployment_etc_path = NULL;
gs_unref_object OstreeSePolicy *sepolicy = NULL;
gboolean etc_exists; gboolean etc_exists;
gboolean usretc_exists; gboolean usretc_exists;
@ -703,21 +518,26 @@ merge_configuration (OstreeSysroot *sysroot,
if (usretc_exists) if (usretc_exists)
{ {
__attribute__((cleanup(ostree_labeling_context_cleanup))) OstreeLabelingContext new_default_secontext = { 0, };
/* TODO - set out labels as we copy files */ /* TODO - set out labels as we copy files */
g_assert (!etc_exists); g_assert (!etc_exists);
if (!gs_shutil_cp_a (deployment_usretc_path, deployment_etc_path, if (!gs_shutil_cp_a (deployment_usretc_path, deployment_etc_path,
cancellable, error)) cancellable, error))
goto out; goto out;
if (!init_labeling_context (deployment_etc_path, &new_default_secontext, /* Here, we initialize SELinux policy from the /usr/etc inside
cancellable, error)) * 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; goto out;
if (!selinux_relabel_dir (sysroot, &new_default_secontext, deployment_etc_path, "etc", if (ostree_sepolicy_get_name (sepolicy) != NULL)
cancellable, error)) {
goto out; 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)); g_print ("ostadmin: Created %s\n", gs_file_get_path_cached (deployment_etc_path));
} }
@ -733,6 +553,7 @@ merge_configuration (OstreeSysroot *sysroot,
} }
ret = TRUE; ret = TRUE;
gs_transfer_out_value (out_sepolicy, &sepolicy);
out: out:
return ret; return ret;
} }
@ -1507,7 +1328,6 @@ ostree_sysroot_deploy_tree (OstreeSysroot *self,
{ {
gboolean ret = FALSE; gboolean ret = FALSE;
gint new_deployserial; gint new_deployserial;
__attribute__((cleanup(ostree_labeling_context_cleanup))) OstreeLabelingContext secontext = { 0, };
gs_unref_object OstreeDeployment *new_deployment = NULL; gs_unref_object OstreeDeployment *new_deployment = NULL;
gs_unref_object OstreeDeployment *merge_deployment = NULL; gs_unref_object OstreeDeployment *merge_deployment = NULL;
gs_unref_object OstreeRepo *repo = 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_kernel_path = NULL;
gs_unref_object GFile *tree_initramfs_path = NULL; gs_unref_object GFile *tree_initramfs_path = NULL;
gs_unref_object GFile *new_deployment_path = NULL; gs_unref_object GFile *new_deployment_path = NULL;
gs_unref_object OstreeSePolicy *sepolicy = NULL;
gs_free char *new_bootcsum = NULL; gs_free char *new_bootcsum = NULL;
gs_unref_object OstreeBootconfigParser *bootconfig = 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, if (!merge_configuration (self, merge_deployment, new_deployment,
new_deployment_path, new_deployment_path,
&sepolicy,
cancellable, error)) cancellable, error))
{ {
g_prefix_error (error, "During /etc merge: "); 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"); deployment_etc = g_file_get_child (new_deployment_path, "etc");
if (!init_labeling_context (deployment_etc, &secontext, cancellable, error)) if (!selinux_relabel_var_if_needed (self, sepolicy, deployment_var,
goto out;
if (!selinux_relabel_var_if_needed (self, &secontext, deployment_var,
cancellable, error)) cancellable, error))
goto out; goto out;

View File

@ -27,6 +27,7 @@
G_BEGIN_DECLS G_BEGIN_DECLS
typedef struct OstreeRepo OstreeRepo; typedef struct OstreeRepo OstreeRepo;
typedef struct OstreeSePolicy OstreeSePolicy;
typedef struct OstreeSysroot OstreeSysroot; typedef struct OstreeSysroot OstreeSysroot;
typedef struct OstreeMutableTree OstreeMutableTree; typedef struct OstreeMutableTree OstreeMutableTree;
typedef struct OstreeRepoFile OstreeRepoFile; typedef struct OstreeRepoFile OstreeRepoFile;