2 # Ensure that cp -Z, -a and cp --preserve=context work properly.
3 # In particular, test on a writable NFS partition.
4 # Check also locally if --preserve=context, -a and --preserve=all
7 # Copyright (C) 2007-2024 Free Software Foundation, Inc.
9 # This program is free software: you can redistribute it and/or modify
10 # it under the terms of the GNU General Public License as published by
11 # the Free Software Foundation, either version 3 of the License, or
12 # (at your option) any later version.
14 # This program is distributed in the hope that it will be useful,
15 # but WITHOUT ANY WARRANTY; without even the implied warranty of
16 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 # GNU General Public License for more details.
19 # You should have received a copy of the GNU General Public License
20 # along with this program. If not, see <https://www.gnu.org/licenses/>.
22 .
"${srcdir=.}/tests/init.sh"; path_prepend_ .
/src
28 cleanup_
() { cd /; umount
"$cwd/mnt"; }
30 # This context is special: it works even when mcstransd isn't running.
31 ctx
='root:object_r:tmp_t'
32 mls_enabled_
&& ctx
="$ctx:s0"
34 # Check basic functionality - before check on fixed context mount
35 touch c || framework_failure_
36 chcon
$ctx c || skip_
"Failed to set context: $ctx"
37 cp -a c d
2>err || framework_failure_
38 cp --preserve=context c e || framework_failure_
39 cp --preserve=all c f || framework_failure_
40 ls -Z d |
grep $ctx || fail
=1
41 # there must be no stderr output for -a
42 compare
/dev
/null err || fail
=1
43 ls -Z e |
grep $ctx || fail
=1
44 ls -Z f |
grep $ctx || fail
=1
47 # Check handling of existing dirs which requires specific handling
48 # due to recursion, and was handled incorrectly in coreutils-8.22
49 # Note standard permissions are updated for existing directories
50 # in the destination, so SELinux contexts should be updated too.
51 mkdir
-p backup
/existing_dir
/ || framework_failure_
52 ls -Zd backup
/existing_dir
> ed_ctx || fail
=1
53 grep $ctx ed_ctx
&& framework_failure_
54 touch backup
/existing_dir
/file || framework_failure_
55 chcon
$ctx backup
/existing_dir
/file || framework_failure_
56 # Set the dir context to ensure it is reset
57 mkdir
-p --context="$ctx" restore
/existing_dir || framework_failure_
58 # Copy and ensure existing directories updated
59 cp -a backup
/. restore
/ || fail
=1
60 ls -Zd restore
/existing_dir
> ed_ctx || fail
=1
62 { ls -lZd restore
/existing_dir
; fail
=1; }
64 # Check context preserved with directories created with --parents,
65 # which was not handled before coreutils-8.27
66 mkdir
-p parents
/a
/b || framework_failure_
67 ls -Zd parents
/a
/b
> ed_ctx || fail
=1
68 grep $ctx ed_ctx
&& framework_failure_
69 touch parents
/a
/b
/file || framework_failure_
70 chcon
$ctx parents
/a
/b || framework_failure_
71 # Set the dir context to ensure it is reset
72 mkdir
-p --context="$ctx" parents_dest
/parents
/a || framework_failure_
73 # Copy and ensure existing directories updated
74 cp -r --parents --preserve=context parents
/a
/b
/file parents_dest || fail
=1
76 ls -Zd parents_dest
/parents
/a
/b
> ed_ctx || fail
=1
78 { ls -lZd parents_dest
/parents
/a
/b
; fail
=1; }
79 # Check updated context
80 ls -Zd parents_dest
/parents
/a
> ed_ctx || fail
=1
82 { ls -lZd parents_dest
/parents
/a
; fail
=1; }
84 # Check restorecon (-Z) functionality for file and directory
85 # Also make a dir with our known context
86 mkdir c_d || framework_failure_
87 chcon
$ctx c_d || framework_failure_
88 # Get the type of this known context for file and dir for tracing
89 old_type_f
=$
(get_selinux_type c
)
90 old_type_d
=$
(get_selinux_type c_d
)
91 # Setup copies for manipulation with restorecon
92 # and get the adjusted type for comparison
94 cp -a c_d Z1_d || fail
=1
95 if restorecon Z1 Z1_d
2>restorecon.err \
96 && compare
/dev
/null restorecon.err
; then
97 new_type_f
=$
(get_selinux_type Z1
)
98 new_type_d
=$
(get_selinux_type Z1_d
)
100 # Ensure -Z sets the type like restorecon does
102 cpZ_type_f
=$
(get_selinux_type Z2
)
103 test "$cpZ_type_f" = "$new_type_f" || fail
=1
105 # Ensure -Z overrides -a and that dirs are handled too
106 cp -aZ c Z3 || fail
=1
107 cp -aZ c_d Z3_d || fail
=1
108 cpaZ_type_f
=$
(get_selinux_type Z3
)
109 cpaZ_type_d
=$
(get_selinux_type Z3_d
)
110 test "$cpaZ_type_f" = "$new_type_f" || fail
=1
111 test "$cpaZ_type_d" = "$new_type_d" || fail
=1
113 # Ensure -Z sets the type for existing files
114 mkdir
-p existing
/c_d || framework_failure_
115 touch existing
/c || framework_failure_
116 cp -aZ c c_d existing || fail
=1
117 cpaZ_type_f
=$
(get_selinux_type existing
/c
)
118 cpaZ_type_d
=$
(get_selinux_type existing
/c_d
)
119 test "$cpaZ_type_f" = "$new_type_f" || fail
=1
120 test "$cpaZ_type_d" = "$new_type_d" || fail
=1
124 # Create a file system, then mount it with the context=... option.
125 dd if=/dev
/zero of
=blob bs
=8192 count
=200 || skip
=1
127 mkfs
-t ext2
-F blob ||
128 skip_
"failed to create an ext2 file system"
130 mount
-oloop,context
=$ctx blob mnt || skip
=1
132 && skip_
"insufficient mount/ext2 support"
134 cd mnt || framework_failure_
136 # Create files with hopefully different contexts
137 echo > ..
/f || framework_failure_
138 echo > g || framework_failure_
139 test "$(stat -c%C ../f)" = "$(stat -c%C g)" &&
140 skip_
"files on separate file systems have the same security context"
142 # /bin/cp from coreutils-6.7-3.fc7 would fail this test by letting cp
143 # succeed (giving no diagnostics), yet leaving the destination file empty.
144 cp -a ..
/f g
2>err || fail
=1
145 test -s g || fail
=1 # The destination file must not be empty.
146 compare
/dev
/null err || fail
=1
148 # =====================================================
149 # Here, we expect cp to succeed and not warn with "Operation not supported"
152 cp --preserve=all ..
/f g
2>err || fail
=1
154 grep "Operation not supported" err
&& fail
=1
156 # =====================================================
157 # The same as above except destination does not exist
159 cp --preserve=all ..
/f g
2>err || fail
=1
161 grep "Operation not supported" err
&& fail
=1
163 # An alternative to the following approach would be to run in a confined
164 # domain (maybe creating/loading it) that lacks the required permissions
166 # Note: this test could also be run by a regular (non-root) user in an
167 # NFS mounted directory. When doing that, I get this diagnostic:
168 # cp: failed to set the security context of 'g' to 'system_u:object_r:nfs_t': \
169 # Operation not supported
170 cat <<\EOF
> exp || framework_failure_
171 cp: failed to
set the security context of
176 # =====================================================
177 # Here, we expect cp to fail, because it cannot set the SELinux
178 # security context through NFS or a mount with fixed context.
179 cp --preserve=context ..
/f g
2> out
&& fail
=1
180 # Here, we *do* expect the destination to be empty.
181 compare
/dev
/null g || fail
=1
182 sed "s/ .g'.*//" out
> k
184 compare exp out || fail
=1
188 # Check if -a option doesn't silence --preserve=context option diagnostics
189 cp -a --preserve=context ..
/f g
2> out2
&& fail
=1
190 # Here, we *do* expect the destination to be empty.
191 compare
/dev
/null g || fail
=1
192 sed "s/ .g'.*//" out2
> k
194 compare exp out2 || fail
=1
196 for no_g_cmd
in '' 'rm -f g'; do
197 # restorecon equivalent. Note even though the context
198 # returned from matchpathcon() will not match $ctx
199 # the resulting ENOTSUP warning will be suppressed.
203 cp -Z ..
/f $
(realpath g
) || fail
=1
206 cp -Z ..
/f g || fail
=1
209 cp -Z -a ..
/f g || fail
=1
210 # -Z doesn't take an arg
212 returns_
1 cp -Z "$ctx" ..
/f g || fail
=1
216 # Explicitly defaulting to the global $ctx should work
217 cp --context="$ctx" ..
/f g || fail
=1
218 # --context overrides -a
220 cp -a --context="$ctx" ..
/f g || fail
=1
223 # Mutually exclusive options
224 returns_
1 cp -Z --preserve=context ..
/f g || fail
=1
225 returns_
1 cp --preserve=context
-Z ..
/f g || fail
=1
226 returns_
1 cp --preserve=context
--context="$ctx" ..
/f g || fail
=1