diff options
author | Pádraig Brady <P@draigBrady.com> | 2013-11-27 12:26:51 +0000 |
---|---|---|
committer | Pádraig Brady <P@draigBrady.com> | 2013-11-27 14:19:32 +0000 |
commit | 7958a4a4fe234f9787daf178a60bc83449605dac (patch) | |
tree | c4cb47f6d62ce69ea1d1f035581c145d35e1d717 /src/copy.c | |
parent | d8e27ab0be8e84ec2287b41dff48073cc13012c3 (diff) | |
download | coreutils-7958a4a4fe234f9787daf178a60bc83449605dac.tar.xz |
selinux: adjust utils to run restorecon with -Z
cp, mv, install, mkdir, mkfifo, mknod are adjusted so that:
-Z no longer accepts an argument.
-Z or --context without an argument do not warn without SELinux.
--context with an argument will warn without SELinux.
* src/local.mk: Reference the new selinux module where required.
* src/system.h: Make the argument to --context optional.
* src/mkdir.c: Likewise. Also handle the SMACK case for --context.
Note we currently silently ignore -Z with SMACK.
* src/mkfifo.c: Likewise.
* src/mknod.c: Likewise.
* src/install.c: Likewise. Note install(1) by default already
set the context for target files to their system default,
albeit with an older method. Use the -Z option to select between
the old and new context restoration behavior, and document
the differences and details for how context restoration
is done in new and old methods, with a view disabling the
old method entirely in future.
* src/cp.c: Make the argument to --context optional.
Note -Z implies --no-preserve=context. I.E. -Z overrides
that aspect of -a no matter what order specified.
(struct cp_options): Document the context handling options.
(main): Check/adjust option combinations after all
options are processed, to both simplify processing
and to make handling independent of order of options
on the command line. Also improve the diagnostics
from a failed call to setfscreatecon().
(set_process_security_ctx): A new function,
refactored to set the default context from the source file,
or with the type adjusted as per the system default for
the destination path.
(set_file_security_ctx): A new function refactored to
set the security context of an existing file, either based on
the process context or the default system context for a path.
(copy_internal): Use the refactored functions to simplify
error handling and consistently fail or warn as needed.
(copy_reg): Likewise.
(copy_internal): With --preserve=context, also copy
context from non regular files. Note for directories this may
impact the copying of subsequent files to that directory?
(copy_attr): If we're handling SELinux explicitly,
then exclude to avoid the redudant copy with --preserve=context,
and the problematic copy with -Z. Note SELinux attribute exclusion
also now honors cp -a --no-preserve=context. Note there was a
very small window over 10 years ago, where attr_copy_file was
available, while attr_copy_check_permissions was not, so we
don't bother adding an explicit m4 check for the latter function.
* src/mv.c: Support --context, but don't allow specifying an argument.
* src/chcon.c: Adjust a comment to be specific to SELinux.
* src/runcon.c: Likewise.
* src/copy.c: Honor the context settings to "restorecon" as appropriate.
* src/copy.h: Add a new setting to select "restorecon" functionality.
* tests/mkdir/selinux.sh: s/-Z/--context=/
* tests/cp/cp-a-selinux.sh: Augment this test with cases
testing basic -Z functionality, and also test the various
invalid option combinations and option precedence.
* tests/mkdir/restorecon.sh: Add a new test for the
more involved mkdir -Z handling, since the directory changing
and non existent directories need to be specially handled.
Also check the similar but simpler handling of -Z by mk{nod,fifo}.
* tests/local.mk: Reference the new test.
* doc/coreutils.texi (cp invocation): Update as per interface changes.
(mv invocation): Likewise.
(install invocation): Likewise.
(mkfifo invocation): Likewise.
(mknod invocation): Likewise.
(mkdir invocation): Likewise.
* NEWS: Mention the new feature and change in behavior.
Diffstat (limited to 'src/copy.c')
-rw-r--r-- | src/copy.c | 222 |
1 files changed, 155 insertions, 67 deletions
diff --git a/src/copy.c b/src/copy.c index bcae12368..dab8fdd77 100644 --- a/src/copy.c +++ b/src/copy.c @@ -61,6 +61,7 @@ #include "write-any-file.h" #include "areadlink.h" #include "yesno.h" +#include "selinux.h" #if USE_XATTR # include <attr/error_context.h> @@ -512,6 +513,18 @@ copy_attr_free (struct error_context *ctx _GL_UNUSED, { } +/* Exclude SELinux extended attributes that are otherwise handled, + and are problematic to copy again. Also honor attributes + configured for exclusion in /etc/xattr.conf. + FIXME: Should we handle POSIX ACLs similarly? + Return zero to skip. */ +static int +check_selinux_attr (const char *name, struct error_context *ctx) +{ + return STRNCMP_LIT (name, "security.selinux") + && attr_copy_check_permissions (name, ctx); +} + /* If positive SRC_FD and DST_FD descriptors are passed, then copy by fd, otherwise copy by name. */ @@ -522,6 +535,7 @@ copy_attr (char const *src_path, int src_fd, int ret; bool all_errors = (!x->data_copy_required || x->require_preserve_xattr); bool some_errors = (!all_errors && !x->reduce_diagnostics); + bool selinux_done = (x->preserve_security_context || x->set_security_context); struct error_context ctx = { .error = all_errors ? copy_attr_allerror : copy_attr_error, @@ -529,10 +543,12 @@ copy_attr (char const *src_path, int src_fd, .quote_free = copy_attr_free }; if (0 <= src_fd && 0 <= dst_fd) - ret = attr_copy_fd (src_path, src_fd, dst_path, dst_fd, 0, + ret = attr_copy_fd (src_path, src_fd, dst_path, dst_fd, + selinux_done ? check_selinux_attr : NULL, (all_errors || some_errors ? &ctx : NULL)); else - ret = attr_copy_file (src_path, dst_path, 0, + ret = attr_copy_file (src_path, dst_path, + selinux_done ? check_selinux_attr : NULL, (all_errors || some_errors ? &ctx : NULL)); return ret == 0; @@ -737,6 +753,96 @@ set_author (const char *dst_name, int dest_desc, const struct stat *src_sb) #endif } +/* Set the default security context for the process. New files will + have this security context set. Also existing files can have their + context adjusted based on this process context, by + set_file_security_ctx() called with PROCESS_LOCAL=true. + This should be called before files are created so there is no race + where a file may be present without an appropriate security context. + Based on CP_OPTIONS, diagnose warnings and fail when appropriate. + Return FALSE on failure, TRUE on success. */ + +static bool +set_process_security_ctx (char const *src_name, char const *dst_name, + mode_t mode, bool new_dst, const struct cp_options *x) +{ + if (x->preserve_security_context) + { + /* Set the default context for the process to match the source. */ + bool all_errors = !x->data_copy_required || x->require_preserve_context; + bool some_errors = !all_errors && !x->reduce_diagnostics; + security_context_t con; + + if (0 <= lgetfilecon (src_name, &con)) + { + if (setfscreatecon (con) < 0) + { + if (all_errors || (some_errors && !errno_unsupported (errno))) + error (0, errno, + _("failed to set default file creation context to %s"), + quote (con)); + if (x->require_preserve_context) + { + freecon (con); + return false; + } + } + freecon (con); + } + else + { + if (all_errors || (some_errors && !errno_unsupported (errno))) + { + error (0, errno, + _("failed to get security context of %s"), + quote (src_name)); + } + if (x->require_preserve_context) + return false; + } + } + else if (x->set_security_context) + { + /* With -Z, adjust the default context for the process + to have the type component adjusted as per the destination path. */ + if (new_dst && defaultcon (dst_name, mode) < 0) + { + if (!errno_unsupported (errno)) + error (0, errno, + _("failed to set default file creation context for %s"), + quote (dst_name)); + } + } + + return true; +} + +/* Reset the security context of DST_NAME, to that already set + as the process default if PROCESS_LOCAL is true. Otherwise + adjust the type component of DST_NAME's security context as + per the system default for that path. Issue warnings upon + failure, when allowed by various settings in CP_OPTIONS. + Return FALSE on failure, TRUE on success. */ + +static bool +set_file_security_ctx (char const *dst_name, bool process_local, + bool recurse, const struct cp_options *x) +{ + bool all_errors = (!x->data_copy_required + || x->require_preserve_context); + bool some_errors = !all_errors && !x->reduce_diagnostics; + + if (! restorecon (dst_name, recurse, process_local)) + { + if (all_errors || (some_errors && !errno_unsupported (errno))) + error (0, errno, _("failed to set the security context of %s"), + quote_n (0, dst_name)); + return false; + } + + return true; +} + /* Change the file mode bits of the file identified by DESC or NAME to MODE. Use DESC if DESC is valid and fchmod is available, NAME otherwise. */ @@ -834,45 +940,24 @@ copy_reg (char const *src_name, char const *dst_name, dest_errno = errno; /* When using cp --preserve=context to copy to an existing destination, - use the default context rather than that of the source. Why? - 1) the src context may prohibit writing, and - 2) because it's more consistent to use the same context - that is used when the destination file doesn't already exist. */ - if (x->preserve_security_context && 0 <= dest_desc) + reset the context as per the default context, which has already been + set according to the src. + When using the mutually exclusive -Z option, then adjust the type of + the existing context according to the system default for the dest. + Note we set the context here, _after_ the file is opened, lest the + new context disallow that. */ + if ((x->set_security_context || x->preserve_security_context) + && 0 <= dest_desc) { - bool all_errors = (!x->data_copy_required - || x->require_preserve_context); - bool some_errors = !all_errors && !x->reduce_diagnostics; - security_context_t con = NULL; - - if (getfscreatecon (&con) < 0) + if (! set_file_security_ctx (dst_name, x->preserve_security_context, + false, x)) { - if (all_errors || (some_errors && !errno_unsupported (errno))) - error (0, errno, _("failed to get file system create context")); if (x->require_preserve_context) { return_val = false; goto close_src_and_dst_desc; } } - - if (con) - { - if (fsetfilecon (dest_desc, con) < 0) - { - if (all_errors || (some_errors && !errno_unsupported (errno))) - error (0, errno, - _("failed to set the security context of %s to %s"), - quote_n (0, dst_name), quote_n (1, con)); - if (x->require_preserve_context) - { - return_val = false; - freecon (con); - goto close_src_and_dst_desc; - } - } - freecon (con); - } } if (dest_desc < 0 && x->unlink_dest_after_failed_open) @@ -888,6 +973,18 @@ copy_reg (char const *src_name, char const *dst_name, /* Tell caller that the destination file was unlinked. */ *new_dst = true; + + /* Ensure there is no race where a file may be left without + an appropriate security context. */ + if (x->set_security_context) + { + if (! set_process_security_ctx (src_name, dst_name, dst_mode, + *new_dst, x)) + { + return_val = false; + goto close_src_desc; + } + } } } @@ -2113,6 +2210,12 @@ copy_internal (char const *src_name, char const *dst_name, emit_verbose (src_name, dst_name, backup_succeeded ? dst_backup : NULL); + if (x->set_security_context) + { + /* -Z failures are only warnings currently. */ + (void) set_file_security_ctx (dst_name, false, true, x); + } + if (rename_succeeded) *rename_succeeded = true; @@ -2222,40 +2325,12 @@ copy_internal (char const *src_name, char const *dst_name, delayed_ok = true; - if (x->preserve_security_context) - { - bool all_errors = !x->data_copy_required || x->require_preserve_context; - bool some_errors = !all_errors && !x->reduce_diagnostics; - security_context_t con; - - if (0 <= lgetfilecon (src_name, &con)) - { - if (setfscreatecon (con) < 0) - { - if (all_errors || (some_errors && !errno_unsupported (errno))) - error (0, errno, - _("failed to set default file creation context to %s"), - quote (con)); - if (x->require_preserve_context) - { - freecon (con); - return false; - } - } - freecon (con); - } - else - { - if (all_errors || (some_errors && !errno_unsupported (errno))) - { - error (0, errno, - _("failed to get security context of %s"), - quote (src_name)); - } - if (x->require_preserve_context) - return false; - } - } + /* If required, set the default security context for new files. + Also for existing files this is used as a reference + when copying the context with --preserve=context. + FIXME: Do we need to consider dst_mode_bits here? */ + if (! set_process_security_ctx (src_name, dst_name, src_mode, new_dst, x)) + return false; if (S_ISDIR (src_mode)) { @@ -2521,6 +2596,19 @@ copy_internal (char const *src_name, char const *dst_name, goto un_backup; } + /* With -Z or --preserve=context, set the context for existing files. + Note this is done already for copy_reg() for reasons described therein. */ + if (!new_dst && !x->copy_as_regular + && (x->set_security_context || x->preserve_security_context)) + { + if (! set_file_security_ctx (dst_name, x->preserve_security_context, + false, x)) + { + if (x->require_preserve_context) + goto un_backup; + } + } + if (command_line_arg && x->dest_info) { /* Now that the destination file is very likely to exist, |