[ostree] SELinux: Ensure we label /var, and fix /etc merge wrt xattrs



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]