[ostree] SELinux: Ensure we label /var, and fix /etc merge wrt xattrs
- From: Colin Walters <walters src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [ostree] SELinux: Ensure we label /var, and fix /etc merge wrt xattrs
- Date: Sun, 2 Feb 2014 16:38:08 +0000 (UTC)
commit e580a88f4ed15b9af3ee9355a3ef5a4dc1cd82ab
Author: Colin Walters <walters verbum org>
Date: Sun Feb 2 11:32:52 2014 -0500
SELinux: Ensure we label /var, and fix /etc merge wrt xattrs
First, /var needs to be labeled at least once. We should probably
rearrange things so that /var is only created (and labeled) on the
first deployment, but this patch adds a /var/.ostree-selabeled file
instead.
Second, when doing the /etc merge, we compare the xattrs of the old
/usr/etc versus the current /etc. The problem with that is that the
policy has different labels for /usr/etc on disk than the real /etc.
The correct fix for this is a bit invasive - we have to take the
physical content of the old /usr/etc, but compare the labels as if
they were really in /etc.
Instead for now, just ignore changes to xattrs. If the file
content/mode changes, then we take the new file (including any changed
xattrs).
Bottom line: just doing chcon -t blah_t /etc/foo.conf may be lost on
upgrade (for now).
src/libostree/ostree-diff.c | 39 +++++--
src/libostree/ostree-diff.h | 8 +-
src/libostree/ostree-sysroot-deploy.c | 210 +++++++++++++++++++++++++++++----
src/ostree/ot-admin-builtin-diff.c | 3 +-
src/ostree/ot-builtin-diff.c | 2 +-
5 files changed, 227 insertions(+), 35 deletions(-)
---
diff --git a/src/libostree/ostree-diff.c b/src/libostree/ostree-diff.c
index 3250fe2..6749b65 100644
--- a/src/libostree/ostree-diff.c
+++ b/src/libostree/ostree-diff.c
@@ -27,7 +27,8 @@
#include "libgsystem.h"
static gboolean
-get_file_checksum (GFile *f,
+get_file_checksum (OstreeDiffFlags flags,
+ GFile *f,
GFileInfo *f_info,
char **out_checksum,
GCancellable *cancellable,
@@ -43,8 +44,25 @@ get_file_checksum (GFile *f,
}
else
{
- if (!ostree_checksum_file (f, OSTREE_OBJECT_TYPE_FILE,
- &csum, cancellable, error))
+ gs_unref_variant GVariant *xattrs = NULL;
+ gs_unref_object GInputStream *in = NULL;
+
+ if (!(flags & OSTREE_DIFF_FLAGS_IGNORE_XATTRS))
+ {
+ if (!gs_file_get_all_xattrs (f, &xattrs, cancellable, error))
+ goto out;
+ }
+
+ if (g_file_info_get_file_type (f_info) == G_FILE_TYPE_REGULAR)
+ {
+ in = (GInputStream*)g_file_read (f, cancellable, error);
+ if (!in)
+ goto out;
+ }
+
+ if (!ostree_checksum_file_from_input (f_info, xattrs, in,
+ OSTREE_OBJECT_TYPE_FILE,
+ &csum, cancellable, error))
goto out;
ret_checksum = ostree_checksum_from_bytes (csum);
}
@@ -101,7 +119,8 @@ diff_item_new (GFile *a,
}
static gboolean
-diff_files (GFile *a,
+diff_files (OstreeDiffFlags flags,
+ GFile *a,
GFileInfo *a_info,
GFile *b,
GFileInfo *b_info,
@@ -114,9 +133,9 @@ diff_files (GFile *a,
gs_free char *checksum_b = NULL;
OstreeDiffItem *ret_item = NULL;
- if (!get_file_checksum (a, a_info, &checksum_a, cancellable, error))
+ if (!get_file_checksum (flags, a, a_info, &checksum_a, cancellable, error))
goto out;
- if (!get_file_checksum (b, b_info, &checksum_b, cancellable, error))
+ if (!get_file_checksum (flags, b, b_info, &checksum_b, cancellable, error))
goto out;
if (strcmp (checksum_a, checksum_b) != 0)
@@ -184,6 +203,7 @@ diff_add_dir_recurse (GFile *d,
/**
* ostree_diff_dirs:
+ * @flags: Flags
* @a: First directory path
* @b: First directory path
* @modified: (element-type OstreeDiffItem): Modified files
@@ -194,7 +214,8 @@ diff_add_dir_recurse (GFile *d,
* sets of #OstreeDiffItem in @modified, @removed, and @added.
*/
gboolean
-ostree_diff_dirs (GFile *a,
+ostree_diff_dirs (OstreeDiffFlags flags,
+ GFile *a,
GFile *b,
GPtrArray *modified,
GPtrArray *removed,
@@ -295,7 +316,7 @@ ostree_diff_dirs (GFile *a,
{
OstreeDiffItem *diff_item = NULL;
- if (!diff_files (child_a, child_a_info, child_b, child_b_info, &diff_item,
+ if (!diff_files (flags, child_a, child_a_info, child_b, child_b_info, &diff_item,
cancellable, error))
goto out;
@@ -304,7 +325,7 @@ ostree_diff_dirs (GFile *a,
if (child_a_type == G_FILE_TYPE_DIRECTORY)
{
- if (!ostree_diff_dirs (child_a, child_b, modified,
+ if (!ostree_diff_dirs (flags, child_a, child_b, modified,
removed, added, cancellable, error))
goto out;
}
diff --git a/src/libostree/ostree-diff.h b/src/libostree/ostree-diff.h
index 35e99f9..6a56e3a 100644
--- a/src/libostree/ostree-diff.h
+++ b/src/libostree/ostree-diff.h
@@ -27,6 +27,11 @@
G_BEGIN_DECLS
+typedef enum {
+ OSTREE_DIFF_FLAGS_NONE = 0,
+ OSTREE_DIFF_FLAGS_IGNORE_XATTRS = (1 << 0)
+} OstreeDiffFlags;
+
typedef struct _OstreeDiffItem OstreeDiffItem;
struct _OstreeDiffItem
{
@@ -47,7 +52,8 @@ void ostree_diff_item_unref (OstreeDiffItem *diffitem);
GType ostree_diff_item_get_type (void);
-gboolean ostree_diff_dirs (GFile *a,
+gboolean ostree_diff_dirs (OstreeDiffFlags flags,
+ GFile *a,
GFile *b,
GPtrArray *modified,
GPtrArray *removed,
diff --git a/src/libostree/ostree-sysroot-deploy.c b/src/libostree/ostree-sysroot-deploy.c
index cef8586..69a2b7f 100644
--- a/src/libostree/ostree-sysroot-deploy.c
+++ b/src/libostree/ostree-sysroot-deploy.c
@@ -126,7 +126,16 @@ merge_etc_changes (GFile *orig_etc,
removed = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref);
added = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref);
- if (!ostree_diff_dirs (orig_etc, modified_etc, modified, removed, added,
+ /* For now, ignore changes to xattrs; the problem is that
+ * security.selinux will be different between the /usr/etc labels
+ * and the ones in the real /etc, so they all show up as different.
+ *
+ * This means that if you want to change the security context of a
+ * file, to have that change persist across upgrades, you must also
+ * modify the content of the file.
+ */
+ if (!ostree_diff_dirs (OSTREE_DIFF_FLAGS_IGNORE_XATTRS,
+ orig_etc, modified_etc, modified, removed, added,
cancellable, error))
{
g_prefix_error (error, "While computing configuration diff: ");
@@ -234,7 +243,7 @@ checkout_deployment_tree (OstreeSysroot *sysroot,
#ifdef HAVE_SELINUX
static gboolean
-get_selinux_policy_root (OstreeSysroot *sysroot,
+get_selinux_policy_root (GFile *deployment_etc,
GFile **out_policy_root,
GCancellable *cancellable,
GError **error)
@@ -250,7 +259,7 @@ get_selinux_policy_root (OstreeSysroot *sysroot,
const char *selinux_prefix = "SELINUX=";
const char *selinuxtype_prefix = "SELINUXTYPE=";
- etc_selinux_dir = g_file_resolve_relative_path (sysroot->path, "etc/selinux");
+ 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))
@@ -435,25 +444,30 @@ relabel_recursively (GFile *dir,
#endif
+typedef struct {
+ gboolean have_policy;
+#ifdef HAVE_SELINUX
+ struct selabel_handle *hnd;
+#endif
+} OstreeLabelingContext;
+
static gboolean
-relabel_etc (OstreeSysroot *sysroot,
- GFile *deployment_etc_path,
- GCancellable *cancellable,
- GError **error)
+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 (sysroot, &policy_root,
+ if (!get_selinux_policy_root (deployment_etc, &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;
+ secontext->have_policy = TRUE;
g_print ("ostadmin: Using SELinux policy '%s'\n", gs_file_get_basename_cached (policy_root));
@@ -465,31 +479,158 @@ relabel_etc (OstreeSysroot *sysroot,
strerror (errno));
goto out;
}
- hnd = selabel_open (SELABEL_CTX_FILE, NULL, 0);
- if (!hnd)
+ 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)
+{
+ if (secontext->hnd)
+ selabel_close (secontext->hnd);
+}
+
+static gboolean
+selinux_relabel_dir (OstreeSysroot *sysroot,
+ OstreeLabelingContext *secontext,
+ 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;
- root_info = g_file_query_info (deployment_etc_path, OSTREE_GIO_FAST_QUERYINFO,
+ 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, "etc");
- if (!relabel_recursively (deployment_etc_path, root_info, path_parts, hnd,
+ 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 /etc: ");
+ g_prefix_error (error, "Relabeling /%s: ", prefix);
goto out;
}
}
- else
- g_print ("ostadmin: No SELinux policy found\n");
+
+ ret = TRUE;
+ out:
+ return ret;
+#else
+ return TRUE;
+#endif
+}
+
+static gboolean
+selinux_relabel_file (OstreeLabelingContext *secontext,
+ GFile *path,
+ const char *prefix,
+ GCancellable *cancellable,
+ GError **error)
+{
+#ifdef HAVE_SELINUX
+ gboolean ret = FALSE;
+
+ if (secontext->have_policy)
+ {
+ 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;
+ }
+ }
+
+ ret = TRUE;
+ out:
+ return ret;
+#else
+ return TRUE;
+#endif
+}
+
+static gboolean
+selinux_relabel_var_if_needed (OstreeSysroot *sysroot,
+ OstreeLabelingContext *secontext,
+ 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");
+
+ 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, 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;
+ }
+ }
ret = TRUE;
out:
@@ -503,9 +644,9 @@ static gboolean
merge_configuration (OstreeSysroot *sysroot,
OstreeDeployment *previous_deployment,
OstreeDeployment *deployment,
- GFile *deployment_path,
- GCancellable *cancellable,
- GError **error)
+ GFile *deployment_path,
+ GCancellable *cancellable,
+ GError **error)
{
gboolean ret = FALSE;
gs_unref_object GFile *source_etc_path = NULL;
@@ -560,11 +701,20 @@ 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 (!relabel_etc (sysroot, deployment_etc_path, cancellable, error))
+
+ if (!init_labeling_context (deployment_etc_path, &new_default_secontext,
+ cancellable, error))
+ goto out;
+
+ if (!selinux_relabel_dir (sysroot, &new_default_secontext, deployment_etc_path, "etc",
+ cancellable, error))
goto out;
g_print ("ostadmin: Created %s\n", gs_file_get_path_cached (deployment_etc_path));
}
@@ -1352,10 +1502,13 @@ 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;
gs_unref_object GFile *osdeploydir = NULL;
+ gs_unref_object GFile *deployment_var = NULL;
+ gs_unref_object GFile *deployment_etc = NULL;
gs_unref_object GFile *commit_root = NULL;
gs_unref_object GFile *tree_kernel_path = NULL;
gs_unref_object GFile *tree_initramfs_path = NULL;
@@ -1376,6 +1529,8 @@ ostree_sysroot_deploy_tree (OstreeSysroot *self,
goto out;
}
+ deployment_var = g_file_get_child (osdeploydir, "var");
+
if (!ostree_sysroot_get_repo (self, &repo, cancellable, error))
goto out;
@@ -1436,6 +1591,15 @@ ostree_sysroot_deploy_tree (OstreeSysroot *self,
goto out;
}
+ 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,
+ cancellable, error))
+ goto out;
+
/* After this, install_deployment_kernel() will set the other boot
* options and write it out to disk.
*/
diff --git a/src/ostree/ot-admin-builtin-diff.c b/src/ostree/ot-admin-builtin-diff.c
index 7497e1a..6623b51 100644
--- a/src/ostree/ot-admin-builtin-diff.c
+++ b/src/ostree/ot-admin-builtin-diff.c
@@ -83,7 +83,8 @@ ot_admin_builtin_diff (int argc, char **argv, OstreeSysroot *sysroot, GCancellab
modified = g_ptr_array_new_with_free_func ((GDestroyNotify) ostree_diff_item_unref);
removed = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref);
added = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref);
- if (!ostree_diff_dirs (orig_etc_path, new_etc_path, modified, removed, added,
+ if (!ostree_diff_dirs (OSTREE_DIFF_FLAGS_IGNORE_XATTRS,
+ orig_etc_path, new_etc_path, modified, removed, added,
cancellable, error))
goto out;
diff --git a/src/ostree/ot-builtin-diff.c b/src/ostree/ot-builtin-diff.c
index 9c26e70..00de8cb 100644
--- a/src/ostree/ot-builtin-diff.c
+++ b/src/ostree/ot-builtin-diff.c
@@ -170,7 +170,7 @@ ostree_builtin_diff (int argc, char **argv, OstreeRepo *repo, GCancellable *canc
removed = g_ptr_array_new_with_free_func ((GDestroyNotify)g_object_unref);
added = g_ptr_array_new_with_free_func ((GDestroyNotify)g_object_unref);
- if (!ostree_diff_dirs (srcf, targetf, modified, removed, added, cancellable, error))
+ if (!ostree_diff_dirs (OSTREE_DIFF_FLAGS_NONE, srcf, targetf, modified, removed, added, cancellable,
error))
goto out;
ostree_diff_print (srcf, targetf, modified, removed, added);
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]