Subject: bug#25378: closed (Re: bug#25378: cp does not preserve SElinx context of sub folder)

From: [email-masked] (GNU bug Tracking System)
Date: Tue, 21 Feb 2017 03:12:02 +0000

Your bug report

#25378: cp does not preserve SElinx context of sub folder

which was filed against the coreutils package, has been closed.

The explanation is attached below, along with your original report.
If you require more details, please reply to [email-masked].

-- 
25378: http://debbugs.gnu.org/cgi/bugreport.cgi?bug=25378
GNU Bug Tracking System
Contact [email-masked] with problems

Subject: Re: bug#25378: cp does not preserve SElinx context of sub folder
From: Pádraig Brady <[email-masked]>
To: HE Henry <[email-masked]>, [email-masked]
Date: Mon, 20 Feb 2017 19:11:15 -0800
List-Id: <debbugs-submit.debbugs.gnu.org>

On 06/01/17 00:19, HE Henry wrote:
> Hi coreutils team,
> 
> When using cp with --parents options, the SELinux context of sub folders are not preserved.
> 
> Example as below:
> 
> 1. Before using cp:
> [root@oame0 etc]# pwd
> /etc
> [root@oame0 etc]# ls -Z selinux/
> -rw-r--r--. root root system_u:object_r:selinux_config_t:s0 config
> -rw-r--r--. root root system_u:object_r:selinux_config_t:s0 semanage.conf
> drwxr-xr-x. root root system_u:object_r:selinux_config_t:s0 targeted
> [root@oame0 etc]#  ls -Z -d selinux/
> drwxr-xr-x. root root system_u:object_r:selinux_config_t:s0 selinux/
> 
> 
> 2. Using cp to copy /etc/selinux/targeted/seusers with full path to /tmp
> [root@oame0 etc]# cp -r --preserve=context --parents selinux/targeted/seusers  /tmp
> 
> 3. After using cp, the SELinux context of sub folder are changed, like selinux, targeted 
> 
> [root@oame0 etc]# ls -Z /tmp/selinux/
> drwx------. root root unconfined_u:object_r:user_tmp_t:s0 targeted
> [root@oame0 etc]# ls -Z -d /tmp/selinux/
> drwx------. root root unconfined_u:object_r:user_tmp_t:s0 /tmp/selinux/
> [root@oame0 etc]# ls -Z -d /tmp/selinux/targeted/
> drwx------. root root unconfined_u:object_r:user_tmp_t:s0 /tmp/selinux/targeted/
> [root@oame0 etc]# ls -Z /tmp/selinux/targeted/   
> -rw-------. root root system_u:object_r:selinux_config_t:s0 seusers

The attached should fix that.

thanks!
Pádraig

>From d17ca013f3aadcf697663830fa9ec34cba551f66 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?P=C3=A1draig=20Brady?= <[email-masked]>
Date: Mon, 20 Feb 2017 18:46:49 -0800
Subject: [PATCH] cp: set SElinux context for --parents directories

* src/copy.h (set_process_security_ctx, set_file_security_ctx):
Export for use in cp.c.
* src/copy.h: Likewise.
* src/cp.c (make_dir_parents_private): Call the exported functions
to set the security context for new and updated directories.
* tests/cp/cp-a-selinux.sh: Add a test case.

Fixes http://bugs.gnu.org/25378
---
 NEWS                     |  4 ++++
 src/copy.c               |  4 ++--
 src/copy.h               |  9 +++++++++
 src/cp.c                 | 20 ++++++++++++++++++++
 tests/cp/cp-a-selinux.sh | 23 +++++++++++++++++++++--
 5 files changed, 56 insertions(+), 4 deletions(-)

diff --git a/NEWS b/NEWS
index 7473e6e..167c376 100644
--- a/NEWS
+++ b/NEWS
@@ -10,6 +10,10 @@ GNU coreutils NEWS                                    -*- outline -*-
 
 ** Bug fixes
 
+  cp --parents will now set an SELinux context for created directories,
+  as appropriate for the -a, --preseve=context, or -Z options.
+  [bug present since SELinux support added in coreutils-6.10]
+
   date again converts from a specified time zone.  Previously output was
   not converted to the local time zone, and remained in the specified one.
   [bug introduced in coreutils-8.26]
diff --git a/src/copy.c b/src/copy.c
index 9dbd536..3711539 100644
--- a/src/copy.c
+++ b/src/copy.c
@@ -889,7 +889,7 @@ set_author (const char *dst_name, int dest_desc, const struct stat *src_sb)
    Based on CP_OPTIONS, diagnose warnings and fail when appropriate.
    Return FALSE on failure, TRUE on success.  */
 
-static bool
+extern bool
 set_process_security_ctx (char const *src_name, char const *dst_name,
                           mode_t mode, bool new_dst, const struct cp_options *x)
 {
@@ -951,7 +951,7 @@ set_process_security_ctx (char const *src_name, char const *dst_name,
    failure, when allowed by various settings in CP_OPTIONS.
    Return FALSE on failure, TRUE on success.  */
 
-static bool
+extern bool
 set_file_security_ctx (char const *dst_name, bool process_local,
                        bool recurse, const struct cp_options *x)
 {
diff --git a/src/copy.h b/src/copy.h
index f7047bc..db65c4a 100644
--- a/src/copy.h
+++ b/src/copy.h
@@ -285,6 +285,15 @@ bool copy (char const *src_name, char const *dst_name,
            bool nonexistent_dst, const struct cp_options *options,
            bool *copy_into_self, bool *rename_succeeded);
 
+bool
+set_process_security_ctx (char const *src_name, char const *dst_name,
+                          mode_t mode, bool new_dst,
+                          const struct cp_options *x);
+
+bool
+set_file_security_ctx (char const *dst_name, bool process_local,
+                       bool recurse, const struct cp_options *x);
+
 void dest_info_init (struct cp_options *);
 void src_info_init (struct cp_options *);
 
diff --git a/src/cp.c b/src/cp.c
index 88db3a3..6e18263 100644
--- a/src/cp.c
+++ b/src/cp.c
@@ -394,6 +394,8 @@ make_dir_parents_private (char const *const_dir, size_t src_offset,
 
   *attr_list = NULL;
 
+  /* XXX: If all dirs present at the destination,
+     no permissions or security contexts will be updated.  */
   if (stat (dst_dir, &stats) != 0)
     {
       /* A parent of CONST_DIR does not exist.
@@ -437,6 +439,12 @@ make_dir_parents_private (char const *const_dir, size_t src_offset,
               *attr_list = new;
             }
 
+          /* If required set the default context for created dirs.  */
+          if (! set_process_security_ctx (src, dir,
+                                          missing_dir ? new->st.st_mode : 0,
+                                          missing_dir, x))
+            return false;
+
           if (missing_dir)
             {
               mode_t src_mode;
@@ -524,6 +532,18 @@ make_dir_parents_private (char const *const_dir, size_t src_offset,
             }
           else
             *new_dst = false;
+
+          /* For existing dirs, set the security context as per that already
+             set for the process global context.  */
+          if (! *new_dst
+              && (x->set_security_context || x->preserve_security_context))
+            {
+              if (! set_file_security_ctx (dir, x->preserve_security_context,
+                                           false, x)
+                  && x->require_preserve_context)
+                  return false;
+            }
+
           *slash++ = '/';
 
           /* Avoid unnecessary calls to 'stat' when given
diff --git a/tests/cp/cp-a-selinux.sh b/tests/cp/cp-a-selinux.sh
index 19a9e64..de07406 100755
--- a/tests/cp/cp-a-selinux.sh
+++ b/tests/cp/cp-a-selinux.sh
@@ -48,7 +48,6 @@ rm -f f
 # due to recursion, and was handled incorrectly in coreutils-8.22
 # Note standard permissions are updated for existing directories
 # in the destination, so SELinux contexts should be updated too.
-chmod o+rw restore/existing_dir
 mkdir -p backup/existing_dir/ || framework_failure_
 ls -Zd backup/existing_dir > ed_ctx || fail=1
 grep $ctx ed_ctx && framework_failure_
@@ -57,11 +56,31 @@ chcon $ctx backup/existing_dir/file || framework_failure_
 # Set the dir context to ensure it is reset
 mkdir -p --context="$ctx" restore/existing_dir || framework_failure_
 # Copy and ensure existing directories updated
-cp -a backup/. restore/
+cp -a backup/. restore/ || fail=1
 ls -Zd restore/existing_dir > ed_ctx || fail=1
 grep $ctx ed_ctx &&
   { ls -lZd restore/existing_dir; fail=1; }
 
+# Check context preserved with directories created with --parents,
+# which was not handled before coreutils-8.27
+mkdir -p parents/a/b || framework_failure_
+ls -Zd parents/a/b > ed_ctx || fail=1
+grep $ctx ed_ctx && framework_failure_
+touch parents/a/b/file || framework_failure_
+chcon $ctx parents/a/b || framework_failure_
+# Set the dir context to ensure it is reset
+mkdir -p --context="$ctx" parents_dest/parents/a || framework_failure_
+# Copy and ensure existing directories updated
+cp -r --parents --preserve=context parents/a/b/file parents_dest || fail=1
+# Check new context
+ls -Zd parents_dest/parents/a/b > ed_ctx || fail=1
+grep $ctx ed_ctx ||
+  { ls -lZd parents_dest/parents/a/b; fail=1; }
+# Check updated context
+ls -Zd parents_dest/parents/a > ed_ctx || fail=1
+grep $ctx ed_ctx &&
+  { ls -lZd parents_dest/parents/a; fail=1; }
+
 # Check restorecon (-Z) functionality for file and directory
 # Also make a dir with our known context
 mkdir c_d || framework_failure_
-- 
2.5.5

Subject: cp does not preserve SElinx context of sub folder
From: HE Henry <[email-masked]>
To: "[email-masked]" <[email-masked]>
Date: Fri, 6 Jan 2017 08:19:11 +0000
List-Id: <debbugs-submit.debbugs.gnu.org>

Hi coreutils team,

When using cp with --parents options, the SELinux context of sub folders are not preserved.

Example as below:

1. Before using cp:
[root@oame0 etc]# pwd
/etc
[root@oame0 etc]# ls -Z selinux/
-rw-r--r--. root root system_u:object_r:selinux_config_t:s0 config
-rw-r--r--. root root system_u:object_r:selinux_config_t:s0 semanage.conf
drwxr-xr-x. root root system_u:object_r:selinux_config_t:s0 targeted
[root@oame0 etc]#  ls -Z -d selinux/
drwxr-xr-x. root root system_u:object_r:selinux_config_t:s0 selinux/

2. Using cp to copy /etc/selinux/targeted/seusers with full path to /tmp
[root@oame0 etc]# cp -r --preserve=context --parents selinux/targeted/seusers  /tmp

3. After using cp, the SELinux context of sub folder are changed, like selinux, targeted 

[root@oame0 etc]# ls -Z /tmp/selinux/
drwx------. root root unconfined_u:object_r:user_tmp_t:s0 targeted
[root@oame0 etc]# ls -Z -d /tmp/selinux/
drwx------. root root unconfined_u:object_r:user_tmp_t:s0 /tmp/selinux/
[root@oame0 etc]# ls -Z -d /tmp/selinux/targeted/
drwx------. root root unconfined_u:object_r:user_tmp_t:s0 /tmp/selinux/targeted/
[root@oame0 etc]# ls -Z /tmp/selinux/targeted/   
-rw-------. root root system_u:object_r:selinux_config_t:s0 seusers

Thanks,
Henry