[ostree] Add SELinux support



commit 2313bdcb62eee1170b128737ca84664cf82b13b8
Author: Colin Walters <walters verbum org>
Date:   Wed Jan 29 09:22:16 2014 -0500

    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.

 configure.ac                          |   26 +++
 packaging/ostree.spec.in              |    1 +
 src/libostree/ostree-repo-commit.c    |    9 +-
 src/libostree/ostree-repo.h           |    1 +
 src/libostree/ostree-sysroot-deploy.c |  274 +++++++++++++++++++++++++++++++++
 5 files changed, 310 insertions(+), 1 deletions(-)
---
diff --git a/configure.ac b/configure.ac
index 7164a40..99c1630 100644
--- a/configure.ac
+++ b/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
diff --git a/packaging/ostree.spec.in b/packaging/ostree.spec.in
index 8c28467..c2596d2 100644
--- a/packaging/ostree.spec.in
+++ b/packaging/ostree.spec.in
@@ -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
diff --git a/src/libostree/ostree-repo-commit.c b/src/libostree/ostree-repo-commit.c
index 7482db7..ad910dc 100644
--- a/src/libostree/ostree-repo-commit.c
+++ b/src/libostree/ostree-repo-commit.c
@@ -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;
 }
 
diff --git a/src/libostree/ostree-repo.h b/src/libostree/ostree-repo.h
index 9521a58..3f311f9 100644
--- a/src/libostree/ostree-repo.h
+++ b/src/libostree/ostree-repo.h
@@ -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);
diff --git a/src/libostree/ostree-sysroot-deploy.c b/src/libostree/ostree-sysroot-deploy.c
index 4a21740..cef8586 100644
--- a/src/libostree/ostree-sysroot-deploy.c
+++ b/src/libostree/ostree-sysroot-deploy.c
@@ -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));
     }
 


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]