mirror of
https://github.com/ostreedev/ostree.git
synced 2025-02-01 09:47:45 +03:00
Add SELinux support
The trees as shipped come with /usr/etc, which should just be labeled as usr_t. When we do a deployment, we need to relabel the copies of the files we're making in /etc. SELinux support is compile and runtime optional.
This commit is contained in:
parent
be1acfafa0
commit
2313bdcb62
26
configure.ac
26
configure.ac
@ -139,6 +139,31 @@ AS_IF([ test x$with_libarchive != xno ], [
|
||||
if test x$with_libarchive != xno; then OSTREE_FEATURES="$OSTREE_FEATURES +libarchive"; fi
|
||||
AM_CONDITIONAL(USE_LIBARCHIVE, test $with_libarchive != no)
|
||||
|
||||
dnl This is what is in RHEL7 anyways
|
||||
SELINUX_DEPENDENCY="libselinux >= 2.2.1"
|
||||
|
||||
AC_ARG_WITH(selinux,
|
||||
AS_HELP_STRING([--without-selinux], [Do not use SELinux]),
|
||||
:, with_selinux=maybe)
|
||||
|
||||
AS_IF([ test x$with_selinux != xno ], [
|
||||
AC_MSG_CHECKING([for $SELINUX_DEPENDENCY])
|
||||
PKG_CHECK_EXISTS($SELINUX_DEPENDENCY, have_selinux=yes, have_selinux=no)
|
||||
AC_MSG_RESULT([$have_selinux])
|
||||
AS_IF([ test x$have_selinux = xno && test x$with_selinux != xmaybe ], [
|
||||
AC_MSG_ERROR([SELinux is enabled but could not be found])
|
||||
])
|
||||
AS_IF([ test x$have_selinux = xyes], [
|
||||
AC_DEFINE(HAVE_SELINUX, 1, [Define if we have libselinux.pc])
|
||||
PKG_CHECK_MODULES(OT_DEP_SELINUX, $SELINUX_DEPENDENCY)
|
||||
with_selinux=yes
|
||||
], [
|
||||
with_selinux=no
|
||||
])
|
||||
], [ with_selinux=no ])
|
||||
if test x$with_selinux != xno; then OSTREE_FEATURES="$OSTREE_FEATURES +selinux"; fi
|
||||
AM_CONDITIONAL(USE_SELINUX, test $with_selinux != no)
|
||||
|
||||
AC_ARG_WITH(dracut,
|
||||
AS_HELP_STRING([--with-dracut],
|
||||
[Install dracut module (default: no)]),,
|
||||
@ -190,6 +215,7 @@ echo "
|
||||
embedded dependencies: $enable_embedded_dependencies
|
||||
introspection: $found_introspection
|
||||
libsoup (retrieve remote HTTP repositories): $with_soup
|
||||
SELinux: $with_selinux
|
||||
libarchive (parse tar files directly): $with_libarchive
|
||||
gpgme (sign commits): $with_gpgme
|
||||
documentation: $enable_gtk_doc
|
||||
|
@ -18,6 +18,7 @@ BuildRequires: pkgconfig(libsoup-2.4)
|
||||
BuildRequires: libattr-devel
|
||||
# Extras
|
||||
BuildRequires: pkgconfig(libarchive)
|
||||
BuildRequires: pkgconfig(libselinux)
|
||||
BuildRequires: gpgme-devel
|
||||
BuildRequires: pkgconfig(systemd)
|
||||
BuildRequires: /usr/bin/g-ir-scanner
|
||||
|
@ -1613,6 +1613,7 @@ struct OstreeRepoCommitModifier {
|
||||
GDestroyNotify destroy_notify;
|
||||
|
||||
OstreeRepoCommitModifierXattrCallback xattr_callback;
|
||||
GDestroyNotify xattr_destroy;
|
||||
gpointer xattr_user_data;
|
||||
};
|
||||
|
||||
@ -2070,6 +2071,9 @@ ostree_repo_commit_modifier_unref (OstreeRepoCommitModifier *modifier)
|
||||
if (modifier->destroy_notify)
|
||||
modifier->destroy_notify (modifier->user_data);
|
||||
|
||||
if (modifier->xattr_destroy)
|
||||
modifier->xattr_destroy (modifier->xattr_user_data);
|
||||
|
||||
g_free (modifier);
|
||||
return;
|
||||
}
|
||||
@ -2078,7 +2082,8 @@ ostree_repo_commit_modifier_unref (OstreeRepoCommitModifier *modifier)
|
||||
* ostree_repo_commit_modifier_set_xattr_callback:
|
||||
* @modifier: An #OstreeRepoCommitModifier
|
||||
* @callback: Function to be invoked, should return extended attributes for path
|
||||
* @user_data: (closure): Data for @callback:
|
||||
* @destroy: Destroy notification
|
||||
* @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
|
||||
@ -2087,9 +2092,11 @@ ostree_repo_commit_modifier_unref (OstreeRepoCommitModifier *modifier)
|
||||
void
|
||||
ostree_repo_commit_modifier_set_xattr_callback (OstreeRepoCommitModifier *modifier,
|
||||
OstreeRepoCommitModifierXattrCallback callback,
|
||||
GDestroyNotify destroy,
|
||||
gpointer user_data)
|
||||
{
|
||||
modifier->xattr_callback = callback;
|
||||
modifier->xattr_destroy = destroy;
|
||||
modifier->xattr_user_data = user_data;
|
||||
}
|
||||
|
||||
|
@ -307,6 +307,7 @@ typedef GVariant *(*OstreeRepoCommitModifierXattrCallback) (OstreeRepo *repo
|
||||
|
||||
void ostree_repo_commit_modifier_set_xattr_callback (OstreeRepoCommitModifier *modifier,
|
||||
OstreeRepoCommitModifierXattrCallback callback,
|
||||
GDestroyNotify destroy,
|
||||
gpointer user_data);
|
||||
|
||||
OstreeRepoCommitModifier *ostree_repo_commit_modifier_ref (OstreeRepoCommitModifier *modifier);
|
||||
|
@ -22,6 +22,11 @@
|
||||
|
||||
#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"
|
||||
@ -227,6 +232,273 @@ checkout_deployment_tree (OstreeSysroot *sysroot,
|
||||
return ret;
|
||||
}
|
||||
|
||||
#ifdef HAVE_SELINUX
|
||||
static gboolean
|
||||
get_selinux_policy_root (OstreeSysroot *sysroot,
|
||||
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_resolve_relative_path (sysroot->path, "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)
|
||||
{
|
||||
GString *path_buf;
|
||||
|
||||
path_buf = g_string_new ("");
|
||||
|
||||
if (path->len == 0)
|
||||
g_string_append_c (path_buf, '/');
|
||||
else
|
||||
{
|
||||
guint i;
|
||||
for (i = 0; i < path->len; i++)
|
||||
{
|
||||
const char *elt = path->pdata[i];
|
||||
|
||||
g_string_append_c (path_buf, '/');
|
||||
g_string_append (path_buf, elt);
|
||||
}
|
||||
}
|
||||
|
||||
return g_string_free (path_buf, FALSE);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
relabel_one_path (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;
|
||||
}
|
||||
}
|
||||
|
||||
ret = TRUE;
|
||||
out:
|
||||
if (con) freecon (con);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
relabel_recursively (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,
|
||||
cancellable, error))
|
||||
goto out;
|
||||
|
||||
direnum = g_file_enumerate_children (dir, OSTREE_GIO_FAST_QUERYINFO,
|
||||
G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
|
||||
cancellable, error);
|
||||
if (!direnum)
|
||||
goto out;
|
||||
|
||||
while (TRUE)
|
||||
{
|
||||
GFileInfo *file_info;
|
||||
GFile *child;
|
||||
GFileType ftype;
|
||||
|
||||
if (!gs_file_enumerator_iterate (direnum, &file_info, &child,
|
||||
cancellable, error))
|
||||
goto out;
|
||||
if (file_info == NULL)
|
||||
break;
|
||||
|
||||
g_ptr_array_add (path_parts, (char*)gs_file_get_basename_cached (child));
|
||||
|
||||
ftype = g_file_info_get_file_type (file_info);
|
||||
if (ftype == G_FILE_TYPE_DIRECTORY)
|
||||
{
|
||||
if (!relabel_recursively (child, file_info, path_parts, hnd,
|
||||
cancellable, error))
|
||||
goto out;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!relabel_one_path (child, file_info, path_parts, hnd,
|
||||
cancellable, error))
|
||||
goto out;
|
||||
}
|
||||
|
||||
g_ptr_array_remove_index (path_parts, path_parts->len - 1);
|
||||
}
|
||||
|
||||
ret = TRUE;
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
static gboolean
|
||||
relabel_etc (OstreeSysroot *sysroot,
|
||||
GFile *deployment_etc_path,
|
||||
GCancellable *cancellable,
|
||||
GError **error)
|
||||
{
|
||||
#ifdef HAVE_SELINUX
|
||||
gboolean ret = FALSE;
|
||||
gs_unref_object GFile *policy_root = NULL;
|
||||
|
||||
if (!get_selinux_policy_root (sysroot, &policy_root,
|
||||
cancellable, error))
|
||||
goto out;
|
||||
|
||||
if (policy_root)
|
||||
{
|
||||
struct selabel_handle *hnd;
|
||||
gs_unref_ptrarray GPtrArray *path_parts = g_ptr_array_new ();
|
||||
gs_unref_object GFileInfo *root_info = NULL;
|
||||
|
||||
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;
|
||||
}
|
||||
hnd = selabel_open (SELABEL_CTX_FILE, NULL, 0);
|
||||
if (!hnd)
|
||||
{
|
||||
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
|
||||
"selabel_open(SELABEL_CTX_FILE): %s",
|
||||
strerror (errno));
|
||||
goto out;
|
||||
}
|
||||
|
||||
root_info = g_file_query_info (deployment_etc_path, OSTREE_GIO_FAST_QUERYINFO,
|
||||
G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
|
||||
cancellable, error);
|
||||
if (!root_info)
|
||||
goto out;
|
||||
|
||||
g_ptr_array_add (path_parts, "etc");
|
||||
if (!relabel_recursively (deployment_etc_path, root_info, path_parts, hnd,
|
||||
cancellable, error))
|
||||
{
|
||||
g_prefix_error (error, "Relabeling /etc: ");
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
else
|
||||
g_print ("ostadmin: No SELinux policy found\n");
|
||||
|
||||
ret = TRUE;
|
||||
out:
|
||||
return ret;
|
||||
#else
|
||||
return TRUE;
|
||||
#endif
|
||||
}
|
||||
|
||||
static gboolean
|
||||
merge_configuration (OstreeSysroot *sysroot,
|
||||
OstreeDeployment *previous_deployment,
|
||||
@ -292,6 +564,8 @@ merge_configuration (OstreeSysroot *sysroot,
|
||||
if (!gs_shutil_cp_a (deployment_usretc_path, deployment_etc_path,
|
||||
cancellable, error))
|
||||
goto out;
|
||||
if (!relabel_etc (sysroot, deployment_etc_path, cancellable, error))
|
||||
goto out;
|
||||
g_print ("ostadmin: Created %s\n", gs_file_get_path_cached (deployment_etc_path));
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user