diff options
-rw-r--r-- | ChangeLog-selinux | 23 | ||||
-rw-r--r-- | src/copy.c | 21 | ||||
-rw-r--r-- | src/copy.h | 22 | ||||
-rw-r--r-- | src/cp.c | 2 | ||||
-rw-r--r-- | src/install.c | 1 | ||||
-rw-r--r-- | src/mv.c | 1 | ||||
-rw-r--r-- | tests/Makefile.am | 2 | ||||
-rw-r--r-- | tests/cp/Makefile.am | 1 | ||||
-rwxr-xr-x | tests/cp/cp-a-selinux | 90 | ||||
-rwxr-xr-x | tests/misc/selinux | 7 | ||||
-rw-r--r-- | tests/selinux | 24 |
11 files changed, 175 insertions, 19 deletions
diff --git a/ChangeLog-selinux b/ChangeLog-selinux index f1eba9708..736fd9ca1 100644 --- a/ChangeLog-selinux +++ b/ChangeLog-selinux @@ -1,3 +1,26 @@ +2007-01-31 Jim Meyering <jim@meyering.net> + + * tests/cp/cp-a-selinux: New file. Test for the bug reported in + <http://bugzilla.redhat.com/219900>. + * tests/cp/Makefile.am (TESTS): Add cp-a-selinux. + + * tests/selinux: New file. + * tests/Makefile.am (EXTRA_DIST): Add selinux. + * tests/misc/selinux: Source the new script, rather than open coding it. + + Change how "cp -a" and "cp --preserve=context" work with SELinux. + Now, cp -a attempts to preserve context, but failure to do so does + not change cp's exit status. However "cp --preserve=context" is + similar, but failure *does* cause cp to exit with nonzero status. + * src/copy.h (struct cp_options) [require_preserve_context]: New member. + * src/copy.c (copy_reg, copy_internal): Implement the above. + * src/mv.c (cp_option_init): Initialize the new member. + * src/install.c (cp_option_init): Likewise. + * src/cp.c (cp_option_init): Likewise. + (decode_preserve_arg): Set it or reset it. + + FIXME: add an on-writable-NFS-only test + 2007-01-20 Jim Meyering <jim@meyering.net> cp, mv, install: add SELinux support, but unlike with the Red Hat diff --git a/src/copy.c b/src/copy.c index f60fa55bd..d2088ae46 100644 --- a/src/copy.c +++ b/src/copy.c @@ -310,8 +310,11 @@ copy_reg (char const *src_name, char const *dst_name, if (getfscreatecon (&con) < 0) { error (0, errno, _("failed to get file system create context")); - return_val = false; - goto close_src_desc; + if (x->require_preserve_context) + { + return_val = false; + goto close_src_desc; + } } if (con) @@ -321,9 +324,12 @@ copy_reg (char const *src_name, char const *dst_name, error (0, errno, _("failed to set the security context of %s to %s"), quote_n (0, dst_name), quote_n (1, con)); - return_val = false; - freecon (con); - goto close_src_desc; + if (x->require_preserve_context) + { + return_val = false; + freecon (con); + goto close_src_desc; + } } freecon(con); } @@ -1590,7 +1596,7 @@ copy_internal (char const *src_name, char const *dst_name, error (0, errno, _("failed to set default file creation context to %s"), quote (con)); - if (x->require_preserve) + if (x->require_preserve_context) { freecon (con); return false; @@ -1605,7 +1611,8 @@ copy_internal (char const *src_name, char const *dst_name, error (0, errno, _("failed to get security context of %s"), quote (src_name)); - return false; + if (x->require_preserve_context) + return false; } } } diff --git a/src/copy.h b/src/copy.h index eab6c8678..0d6233f95 100644 --- a/src/copy.h +++ b/src/copy.h @@ -127,9 +127,6 @@ struct cp_options bool preserve_ownership; bool preserve_mode; bool preserve_timestamps; - /* If true, attempt to preserve the SELinux security context, too. - Set this only if the kernel is SELinux enabled. */ - bool preserve_security_context; /* Enabled for mv, and for cp by the --preserve=links option. If true, attempt to preserve in the destination files any @@ -145,10 +142,25 @@ struct cp_options /* If true and any of the above (for preserve) file attributes cannot be applied to a destination file, treat it as a failure and return - nonzero immediately. E.g. cp -p requires this be nonzero, mv requires - it be zero. */ + nonzero immediately. E.g. for cp -p this must be true, for mv it + must be false. */ bool require_preserve; + /* If true, attempt to preserve the SELinux security context, too. + Set this only if the kernel is SELinux enabled. */ + bool preserve_security_context; + + /* Useful only when preserve_security_context is true. + If true, a failed attempt to preserve a file's security context + propagates failure "out" to the caller. If false, a failure to + preserve a file's security context does not change the invoking + application's exit status. Give diagnostics for failed syscalls + regardless of this setting. For example, with "cp --preserve=context" + this flag is "true", while with "cp -a", it is false. That means + "cp -a" attempts to preserve any security context, but does not + fail if it is unable to do so. */ + bool require_preserve_context; + /* If true, copy directories recursively and copy special files as themselves rather than copying their contents. */ bool recursive; @@ -754,6 +754,7 @@ cp_option_init (struct cp_options *x) x->preserve_mode = false; x->preserve_timestamps = false; x->preserve_security_context = false; + x->require_preserve_context = false; x->require_preserve = false; x->recursive = false; @@ -832,6 +833,7 @@ decode_preserve_arg (char const *arg, struct cp_options *x, bool on_off) case PRESERVE_CONTEXT: x->preserve_security_context = on_off; + x->require_preserve_context = on_off; break; case PRESERVE_ALL: diff --git a/src/install.c b/src/install.c index f6152f355..dc7ff0141 100644 --- a/src/install.c +++ b/src/install.c @@ -174,6 +174,7 @@ cp_option_init (struct cp_options *x) x->preserve_mode = false; x->preserve_timestamps = false; x->require_preserve = false; + x->require_preserve_context = false; x->recursive = false; x->sparse_mode = SPARSE_AUTO; x->symbolic_link = false; @@ -131,6 +131,7 @@ cp_option_init (struct cp_options *x) x->preserve_timestamps = true; x->preserve_security_context = selinux_enabled; x->require_preserve = false; /* FIXME: maybe make this an option */ + x->require_preserve_context = false; x->recursive = true; x->sparse_mode = SPARSE_AUTO; /* FIXME: maybe make this an option */ x->symbolic_link = false; diff --git a/tests/Makefile.am b/tests/Makefile.am index b7e30cc66..b2cbbfc09 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -18,7 +18,7 @@ EXTRA_DIST = \ $(TESTS) Coreutils.pm Makefile.am.in README acl envvar-check \ expensive group-names input-tty lang-default mk-script \ other-fs-tmpdir priv-check \ - rwx-to-mode sample-test setgid-check sparse-file \ + rwx-to-mode sample-test selinux setgid-check sparse-file \ umask-check very-expensive ## N O T E :: Please do not add new tests/ directories. diff --git a/tests/cp/Makefile.am b/tests/cp/Makefile.am index f9925dbc4..536c0221d 100644 --- a/tests/cp/Makefile.am +++ b/tests/cp/Makefile.am @@ -18,6 +18,7 @@ # 02110-1301, USA. TESTS = \ + cp-a-selinux \ file-perm-race parent-perm-race \ backup-dir \ src-base-dot \ diff --git a/tests/cp/cp-a-selinux b/tests/cp/cp-a-selinux new file mode 100755 index 000000000..d28b333e0 --- /dev/null +++ b/tests/cp/cp-a-selinux @@ -0,0 +1,90 @@ +#!/bin/sh +# Ensure that cp -a and cp --preserve=context work properly. +# In particular, test on a writable NFS partition. + +# Copyright (C) 2007 Free Software Foundation, Inc. + +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +# 02110-1301, USA. + +if test "$VERBOSE" = yes; then + set -x + cp --version +fi + +. $srcdir/../envvar-check +. $srcdir/../lang-default +. $srcdir/../selinux +PRIV_CHECK_ARG=require-non-root . $srcdir/../priv-check + +pwd=`pwd` +t0=`echo "$0"|sed 's,.*/,,'`.tmp; tmp=$t0/$$ +trap 'status=$?; cd "$pwd" && chmod -R u+rwx $t0 && rm -rf $t0 && exit $status' 0 +trap '(exit $?); exit $?' 1 2 13 15 + +framework_failure=0 +mkdir -p $tmp || framework_failure=1 +cd $tmp || framework_failure=1 + +echo > f || framework_failure=1 +echo > g || framework_failure=1 + +if test $framework_failure = 1; then + echo "$0: failure in testing framework" 1>&2 + (exit 1); exit 1 +fi + +fail=0 + +# /bin/cp from coreutils-6.7-3.fc7 would fail this test by letting cp +# succeed (giving no diagnostics), yet leaving the destination file empty. +cp -a f g 2>err || fail=1 +test -s g || fail=1 # The destination file must not be empty. +test -s err && fail=1 # There must be no stderr output. + +rm -f g err +echo > g + +# ===================================================== +# Here, we expect cp to fail, because it (currently?) cannot +# set the SELinux security context through NFS. +cp --preserve=context f g 2> out && fail=1 + +# Here, we *do* expect the destination to be empty. +test -s g && fail=1 + +# FIXME: currently, this test must be run in an NFS mounted +# directory, and that's not checked. Move this part into a separate +# test and make that a prerequisite. +# In addition, we can add a root-only test that takes one of two +# approaches: 1) create a loopback context=... mount and run the test there. +# 2) run in a confined domain (maybe creating/loading it) that lacks the +# required permissions to the file type). + +# Currently, I get this diagnostic: +# cp: failed to set the security context of `g' to `system_u:object_r:nfs_t': \ +# Operation not supported +# but don't want to depend on ENOTSUP or that specific context triple: +sed "s/ .g' to .*//" out > k +mv k out + +cat <<\EOF > exp || fail=1 +cp: failed to set the security context of +EOF + +cmp out exp || fail=1 +test $fail = 1 && diff out exp 2> /dev/null + +(exit $fail); exit $fail diff --git a/tests/misc/selinux b/tests/misc/selinux index b8499842b..3e2aae5e2 100755 --- a/tests/misc/selinux +++ b/tests/misc/selinux @@ -12,14 +12,9 @@ fi . $srcdir/../envvar-check . $srcdir/../lang-default +. $srcdir/../selinux PRIV_CHECK_ARG=require-non-root . $srcdir/../priv-check -test "`ls -Zd .`" = '? .' && - { - echo "$0: skipping this test; this system lacks SELinux support" 1>&2 - exit 77 - } - pwd=`pwd` t0=`echo "$0"|sed 's,.*/,,'`.tmp; tmp=$t0/$$ trap 'status=$?; cd $pwd; chmod -R u+rwx $t0; rm -rf $t0 && exit $status' 0 diff --git a/tests/selinux b/tests/selinux new file mode 100644 index 000000000..1c4d8a8a6 --- /dev/null +++ b/tests/selinux @@ -0,0 +1,24 @@ +# Is a test expensive? +# Copyright (C) 2007 Free Software Foundation, Inc. + +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +# 02110-1301, USA. + +test "`ls -Zd .`" = '? .' && + { + echo "$0: skipping this test; this system (or maybe just" 1>&2 + echo " the current file system) lacks SELinux support" 1>&2 + (exit 77); exit 77 + } |