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 | |
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')
-rw-r--r-- | src/chcon.c | 2 | ||||
-rw-r--r-- | src/copy.c | 222 | ||||
-rw-r--r-- | src/copy.h | 3 | ||||
-rw-r--r-- | src/cp.c | 64 | ||||
-rw-r--r-- | src/id.c | 6 | ||||
-rw-r--r-- | src/install.c | 53 | ||||
-rw-r--r-- | src/local.mk | 16 | ||||
-rw-r--r-- | src/mkdir.c | 86 | ||||
-rw-r--r-- | src/mkfifo.c | 52 | ||||
-rw-r--r-- | src/mknod.c | 31 | ||||
-rw-r--r-- | src/mv.c | 15 | ||||
-rw-r--r-- | src/runcon.c | 9 | ||||
-rw-r--r-- | src/system.h | 2 |
13 files changed, 421 insertions, 140 deletions
diff --git a/src/chcon.c b/src/chcon.c index 56f2caa5f..a59f8e2cf 100644 --- a/src/chcon.c +++ b/src/chcon.c @@ -355,7 +355,7 @@ Usage: %s [OPTION]... CONTEXT FILE...\n\ "), program_name, program_name, program_name); fputs (_("\ -Change the security context of each FILE to CONTEXT.\n\ +Change the SELinux security context of each FILE to CONTEXT.\n\ With --reference, change the security context of each FILE to that of RFILE.\n\ "), stdout); 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, diff --git a/src/copy.h b/src/copy.h index cf72d3cca..4918d148f 100644 --- a/src/copy.h +++ b/src/copy.h @@ -159,6 +159,9 @@ struct cp_options bool preserve_timestamps; bool explicit_no_preserve_mode; + /* If true, attempt to set specified security context */ + bool set_security_context; + /* Enabled for mv, and for cp by the --preserve=links option. If true, attempt to preserve in the destination files any logical hard links between the source files. If used with cp's @@ -141,6 +141,7 @@ static struct option const long_opts[] = {"target-directory", required_argument, NULL, 't'}, {"update", no_argument, NULL, 'u'}, {"verbose", no_argument, NULL, 'v'}, + {GETOPT_SELINUX_CONTEXT_OPTION_DECL}, {GETOPT_HELP_OPTION_DECL}, {GETOPT_VERSION_OPTION_DECL}, {NULL, 0, NULL, 0} @@ -228,6 +229,10 @@ Copy SOURCE to DEST, or multiple SOURCE(s) to DIRECTORY.\n\ -v, --verbose explain what is being done\n\ -x, --one-file-system stay on this file system\n\ "), stdout); + fputs (_("\ + -Z, --context[=CTX] set SELinux security context of destination\n\ + file to default type, or to CTX if specified\n\ +"), stdout); fputs (HELP_OPTION_DESCRIPTION, stdout); fputs (VERSION_OPTION_DESCRIPTION, stdout); fputs (_("\ @@ -782,8 +787,9 @@ cp_option_init (struct cp_options *x) x->preserve_mode = false; x->preserve_timestamps = false; x->explicit_no_preserve_mode = false; - x->preserve_security_context = false; - x->require_preserve_context = false; + x->preserve_security_context = false; /* -a or --preserve=context. */ + x->require_preserve_context = false; /* --preserve=context. */ + x->set_security_context = false; /* -Z, set sys default context. */ x->preserve_xattr = false; x->reduce_diagnostics = false; x->require_preserve_xattr = false; @@ -876,8 +882,8 @@ decode_preserve_arg (char const *arg, struct cp_options *x, bool on_off) break; case PRESERVE_CONTEXT: - x->preserve_security_context = on_off; x->require_preserve_context = on_off; + x->preserve_security_context = on_off; break; case PRESERVE_XATTR: @@ -918,6 +924,7 @@ main (int argc, char **argv) bool copy_contents = false; char *target_directory = NULL; bool no_target_directory = false; + security_context_t scontext = NULL; initialize_main (&argc, &argv); set_program_name (argv[0]); @@ -934,7 +941,7 @@ main (int argc, char **argv) we'll actually use backup_suffix_string. */ backup_suffix_string = getenv ("SIMPLE_BACKUP_SUFFIX"); - while ((c = getopt_long (argc, argv, "abdfHilLnprst:uvxPRS:T", + while ((c = getopt_long (argc, argv, "abdfHilLnprst:uvxPRS:TZ", long_opts, NULL)) != -1) { @@ -1092,6 +1099,24 @@ main (int argc, char **argv) x.one_file_system = true; break; + + case 'Z': + /* politely decline if we're not on a selinux-enabled kernel. */ + if (selinux_enabled) + { + if (optarg) + scontext = optarg; + else + x.set_security_context = true; + } + else if (optarg) + { + error (0, 0, + _("warning: ignoring --context; " + "it requires an SELinux-enabled kernel")); + } + break; + case 'S': make_backups = true; backup_suffix_string = optarg; @@ -1150,13 +1175,30 @@ main (int argc, char **argv) if (x.unlink_dest_after_failed_open && (x.hard_link || x.symbolic_link)) x.unlink_dest_before_opening = true; - if (x.preserve_security_context) - { - if (!selinux_enabled) - error (EXIT_FAILURE, 0, - _("cannot preserve security context " - "without an SELinux-enabled kernel")); - } + /* Ensure -Z overrides -a. */ + if ((x.set_security_context || scontext) + && ! x.require_preserve_context) + x.preserve_security_context = false; + + if (x.preserve_security_context && (x.set_security_context || scontext)) + error (EXIT_FAILURE, 0, + _("cannot set target context and preserve it")); + + if (x.require_preserve_context && ! selinux_enabled) + error (EXIT_FAILURE, 0, + _("cannot preserve security context " + "without an SELinux-enabled kernel")); + + /* FIXME: This handles new files. But what about existing files? + I.E. if updating a tree, new files would have the specified context, + but shouldn't existing files be updated for consistency like this? + if (scontext) + restorecon (dst_path, 0, true); + */ + if (scontext && setfscreatecon (optarg) < 0) + error (EXIT_FAILURE, errno, + _("failed to set default file creation context to %s"), + quote (optarg)); #if !USE_XATTR if (x.require_preserve_xattr) @@ -40,8 +40,8 @@ proper_name ("Arnold Robbins"), \ proper_name ("David MacKenzie") -/* If nonzero, output only the SELinux context. -Z */ -static int just_context = 0; +/* If nonzero, output only the SELinux context. */ +static bool just_context = 0; static void print_user (uid_t uid); static void print_full_info (const char *username); @@ -155,7 +155,7 @@ main (int argc, char **argv) error (EXIT_FAILURE, 0, _("--context (-Z) works only on an SELinux-enabled kernel")); #endif - just_context = 1; + just_context = true; break; case 'g': diff --git a/src/install.c b/src/install.c index a5ed7a821..707665526 100644 --- a/src/install.c +++ b/src/install.c @@ -279,7 +279,6 @@ cp_option_init (struct cp_options *x) x->reduce_diagnostics=false; x->data_copy_required = true; x->require_preserve = false; - x->require_preserve_context = false; x->require_preserve_xattr = false; x->recursive = false; x->sparse_mode = SPARSE_AUTO; @@ -295,7 +294,9 @@ cp_option_init (struct cp_options *x) x->open_dangling_dest_symlink = false; x->update = false; - x->preserve_security_context = false; + x->require_preserve_context = false; /* Not used by install currently. */ + x->preserve_security_context = false; /* Whether to copy context from src. */ + x->set_security_context = false; /* Whether to set sys default context. */ x->preserve_xattr = false; x->verbose = false; x->dest_info = NULL; @@ -305,7 +306,8 @@ cp_option_init (struct cp_options *x) #ifdef ENABLE_MATCHPATHCON /* Modify file context to match the specified policy. If an error occurs the file will remain with the default directory - context. */ + context. Note this sets the context to that returned by matchpathcon, + and thus discards MLS levels and user identity of the FILE. */ static void setdefaultfilecon (char const *file) { @@ -359,7 +361,8 @@ setdefaultfilecon (char const *file) first_call = false; /* If there's an error determining the context, or it has none, - return to allow default context */ + return to allow default context. Note the "<<none>>" check + is only needed for libselinux < 1.20 (2005-01-04). */ if ((matchpathcon (file, st.st_mode, &scontext) != 0) || STREQ (scontext, "<<none>>")) { @@ -644,8 +647,8 @@ In the 4th form, create all components of the given DIRECTORY(ies).\n\ "), stdout); fputs (_("\ --preserve-context preserve SELinux security context\n\ - -Z, --context=CONTEXT set SELinux security context of files and directories\ -\n\ + -Z, --context[=CTX] set SELinux security context of destination file to\n\ + default type, or to CTX if specified\n\ "), stdout); fputs (HELP_OPTION_DESCRIPTION, stdout); @@ -791,7 +794,7 @@ main (int argc, char **argv) we'll actually use backup_suffix_string. */ backup_suffix_string = getenv ("SIMPLE_BACKUP_SUFFIX"); - while ((optc = getopt_long (argc, argv, "bcCsDdg:m:o:pt:TvS:Z:", long_options, + while ((optc = getopt_long (argc, argv, "bcCsDdg:m:o:pt:TvS:Z", long_options, NULL)) != -1) { switch (optc) @@ -863,7 +866,7 @@ main (int argc, char **argv) break; case PRESERVE_CONTEXT_OPTION: - if ( ! selinux_enabled) + if (! selinux_enabled) { error (0, 0, _("WARNING: ignoring --preserve-context; " "this kernel is not SELinux-enabled")); @@ -873,14 +876,27 @@ main (int argc, char **argv) use_default_selinux_context = false; break; case 'Z': - if ( ! selinux_enabled) + if (selinux_enabled) { - error (0, 0, _("WARNING: ignoring --context (-Z); " - "this kernel is not SELinux-enabled")); - break; + /* Disable use of the install(1) specific setdefaultfilecon(). + Note setdefaultfilecon() is different from the newer and more + generic restorecon() in that the former sets the context of + the dest files to that returned by matchpathcon directly, + thus discarding MLS level and user identity of the file. + TODO: consider removing setdefaultfilecon() in future. */ + use_default_selinux_context = false; + + if (optarg) + scontext = optarg; + else + x.set_security_context = true; + } + else if (optarg) + { + error (0, 0, + _("warning: ignoring --context; " + "it requires an SELinux-enabled kernel")); } - scontext = optarg; - use_default_selinux_context = false; break; case_GETOPT_HELP_CHAR; case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS); @@ -897,11 +913,6 @@ main (int argc, char **argv) error (EXIT_FAILURE, 0, _("target directory not allowed when installing a directory")); - if (x.preserve_security_context && scontext != NULL) - error (EXIT_FAILURE, 0, - _("cannot force target context to %s and preserve it"), - quote (scontext)); - if (backup_suffix_string) simple_backup_suffix = xstrdup (backup_suffix_string); @@ -910,6 +921,10 @@ main (int argc, char **argv) version_control_string) : no_backups); + if (x.preserve_security_context && (x.set_security_context || scontext)) + error (EXIT_FAILURE, 0, + _("cannot set target context and preserve it")); + if (scontext && setfscreatecon (scontext) < 0) error (EXIT_FAILURE, errno, _("failed to set default file creation context to %s"), diff --git a/src/local.mk b/src/local.mk index 646fbada1..1315e1103 100644 --- a/src/local.mk +++ b/src/local.mk @@ -312,6 +312,10 @@ RELEASE_YEAR = \ `sed -n '/.*COPYRIGHT_YEAR = \([0-9][0-9][0-9][0-9]\) };/s//\1/p' \ $(top_srcdir)/lib/version-etc.c` +selinux_sources = \ + src/selinux.c \ + src/selinux.h + copy_sources = \ src/copy.c \ src/cp-hash.c \ @@ -323,12 +327,13 @@ copy_sources = \ # to install before applying any user-specified name transformations. transform = s/ginstall/install/; $(program_transform_name) -src_ginstall_SOURCES = src/install.c src/prog-fprintf.c $(copy_sources) +src_ginstall_SOURCES = src/install.c src/prog-fprintf.c $(copy_sources) \ + $(selinux_sources) # This is for the '[' program. Automake transliterates '[' and '/' to '_'. src___SOURCES = src/lbracket.c -src_cp_SOURCES = src/cp.c $(copy_sources) +src_cp_SOURCES = src/cp.c $(copy_sources) $(selinux_sources) src_dir_SOURCES = src/ls.c src/ls-dir.c src_vdir_SOURCES = src/ls.c src/ls-vdir.c src_id_SOURCES = src/id.c src/group-list.c @@ -341,12 +346,15 @@ src_kill_SOURCES = src/kill.c src/operand2sig.c src_realpath_SOURCES = src/realpath.c src/relpath.c src/relpath.h src_timeout_SOURCES = src/timeout.c src/operand2sig.c -src_mv_SOURCES = src/mv.c src/remove.c $(copy_sources) +src_mv_SOURCES = src/mv.c src/remove.c $(copy_sources) $(selinux_sources) src_rm_SOURCES = src/rm.c src/remove.c -src_mkdir_SOURCES = src/mkdir.c src/prog-fprintf.c +src_mkdir_SOURCES = src/mkdir.c src/prog-fprintf.c $(selinux_sources) src_rmdir_SOURCES = src/rmdir.c src/prog-fprintf.c +src_mkfifo_SOURCES = src/mkfifo.c $(selinux_sources) +src_mknod_SOURCES = src/mknod.c $(selinux_sources) + src_df_SOURCES = src/df.c src/find-mount-point.c src_stat_SOURCES = src/stat.c src/find-mount-point.c diff --git a/src/mkdir.c b/src/mkdir.c index efd318497..25b1da5e7 100644 --- a/src/mkdir.c +++ b/src/mkdir.c @@ -29,6 +29,7 @@ #include "prog-fprintf.h" #include "quote.h" #include "savewd.h" +#include "selinux.h" #include "smack.h" /* The official name of this program (e.g., no 'g' prefix). */ @@ -65,8 +66,8 @@ Create the DIRECTORY(ies), if they do not already exist.\n\ -m, --mode=MODE set file mode (as in chmod), not a=rwx - umask\n\ -p, --parents no error if existing, make parent directories as needed\n\ -v, --verbose print a message for each created directory\n\ - -Z, --context=CTX set the SELinux security context of each created\n\ - directory to CTX\n\ + -Z, --context[=CTX] set the SELinux security context of each created\n\ + directory to default type or to CTX if specified\n\ "), stdout); fputs (HELP_OPTION_DESCRIPTION, stdout); fputs (VERSION_OPTION_DESCRIPTION, stdout); @@ -91,6 +92,9 @@ struct mkdir_options /* File mode bits affected by MODE. */ mode_t mode_bits; + /* Set the SELinux File Context. */ + bool set_security_context; + /* If not null, format to use when reporting newly made directories. */ char const *created_directory_format; }; @@ -113,12 +117,16 @@ static int make_ancestor (char const *dir, char const *component, void *options) { struct mkdir_options const *o = options; - int r; + + if (o->set_security_context && defaultcon (dir, S_IFDIR) < 0) + error (0, errno, _("failed to set default creation context for %s"), + quote (dir)); + mode_t user_wx = S_IWUSR | S_IXUSR; bool self_denying_umask = (o->umask_value & user_wx) != 0; if (self_denying_umask) umask (o->umask_value & ~user_wx); - r = mkdir (component, S_IRWXUGO); + int r = mkdir (component, S_IRWXUGO); if (self_denying_umask) { int mkdir_errno = errno; @@ -138,11 +146,46 @@ static int process_dir (char *dir, struct savewd *wd, void *options) { struct mkdir_options const *o = options; - return (make_dir_parents (dir, wd, o->make_ancestor_function, options, - o->mode, announce_mkdir, - o->mode_bits, (uid_t) -1, (gid_t) -1, true) - ? EXIT_SUCCESS - : EXIT_FAILURE); + bool set_defaultcon = false; + + /* If possible set context before DIR created. */ + if (o->set_security_context) + { + if (! o->make_ancestor_function) + set_defaultcon = true; + else + { + char *pdir = dir_name (dir); + struct stat st; + if (STREQ (pdir, ".") + || (stat (pdir, &st) == 0 && S_ISDIR (st.st_mode))) + set_defaultcon = true; + free (pdir); + } + if (set_defaultcon && defaultcon (dir, S_IFDIR) < 0) + error (0, errno, _("failed to set default creation context for %s"), + quote (dir)); + } + + int ret = (make_dir_parents (dir, wd, o->make_ancestor_function, options, + o->mode, announce_mkdir, + o->mode_bits, (uid_t) -1, (gid_t) -1, true) + ? EXIT_SUCCESS + : EXIT_FAILURE); + + /* FIXME: Due to the current structure of make_dir_parents() + we don't have the facility to call defaultcon() before the + final component of DIR is created. So for now, create the + final component with the context from previous component + and here we set the context for the final component. */ + if (ret == EXIT_SUCCESS && o->set_security_context && ! set_defaultcon) + { + if (restorecon (last_component (dir), false, false) < 0) + error (0, errno, _("failed to set restore context for %s"), + quote (dir)); + } + + return ret; } int @@ -157,6 +200,7 @@ main (int argc, char **argv) options.mode = S_IRWXUGO; options.mode_bits = 0; options.created_directory_format = NULL; + options.set_security_context = false; initialize_main (&argc, &argv); set_program_name (argv[0]); @@ -166,7 +210,7 @@ main (int argc, char **argv) atexit (close_stdout); - while ((optc = getopt_long (argc, argv, "pm:vZ:", longopts, NULL)) != -1) + while ((optc = getopt_long (argc, argv, "pm:vZ", longopts, NULL)) != -1) { switch (optc) { @@ -180,7 +224,24 @@ main (int argc, char **argv) options.created_directory_format = _("created directory %s"); break; case 'Z': - scontext = optarg; + if (is_smack_enabled ()) + { + /* We don't yet support -Z to restore context with SMACK. */ + scontext = optarg; + } + else if (is_selinux_enabled () > 0) + { + if (optarg) + scontext = optarg; + else + options.set_security_context = true; + } + else if (optarg) + { + error (0, 0, + _("warning: ignoring --context; " + "it requires an SELinux/SMACK-enabled kernel")); + } break; case_GETOPT_HELP_CHAR; case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS); @@ -195,6 +256,9 @@ main (int argc, char **argv) usage (EXIT_FAILURE); } + /* FIXME: This assumes mkdir() is done in the same process. + If that's not always the case we would need to call this + like we do when options.set_security_context == true. */ if (scontext) { int ret = 0; diff --git a/src/mkfifo.c b/src/mkfifo.c index 4c6dac468..df97d6baa 100644 --- a/src/mkfifo.c +++ b/src/mkfifo.c @@ -26,6 +26,7 @@ #include "error.h" #include "modechange.h" #include "quote.h" +#include "selinux.h" #include "smack.h" /* The official name of this program (e.g., no 'g' prefix). */ @@ -60,7 +61,8 @@ Create named pipes (FIFOs) with the given NAMEs.\n\ -m, --mode=MODE set file permission bits to MODE, not a=rw - umask\n\ "), stdout); fputs (_("\ - -Z, --context=CTX set the SELinux security context of each NAME to CTX\n\ + -Z, --context[=CTX] set the SELinux security context of each NAME to\n\ + default type, or CTX if specified\n\ "), stdout); fputs (HELP_OPTION_DESCRIPTION, stdout); fputs (VERSION_OPTION_DESCRIPTION, stdout); @@ -77,6 +79,7 @@ main (int argc, char **argv) int exit_status = EXIT_SUCCESS; int optc; security_context_t scontext = NULL; + bool set_security_context = false; initialize_main (&argc, &argv); set_program_name (argv[0]); @@ -86,7 +89,7 @@ main (int argc, char **argv) atexit (close_stdout); - while ((optc = getopt_long (argc, argv, "m:Z:", longopts, NULL)) != -1) + while ((optc = getopt_long (argc, argv, "m:Z", longopts, NULL)) != -1) { switch (optc) { @@ -94,7 +97,24 @@ main (int argc, char **argv) specified_mode = optarg; break; case 'Z': - scontext = optarg; + if (is_smack_enabled ()) + { + /* We don't yet support -Z to restore context with SMACK. */ + scontext = optarg; + } + else if (is_selinux_enabled () > 0) + { + if (optarg) + scontext = optarg; + else + set_security_context = true; + } + else if (optarg) + { + error (0, 0, + _("warning: ignoring --context; " + "it requires an SELinux/SMACK-enabled kernel")); + } break; case_GETOPT_HELP_CHAR; case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS); @@ -140,17 +160,21 @@ main (int argc, char **argv) } for (; optind < argc; ++optind) - if (mkfifo (argv[optind], newmode) != 0) - { - error (0, errno, _("cannot create fifo %s"), quote (argv[optind])); - exit_status = EXIT_FAILURE; - } - else if (specified_mode && lchmod (argv[optind], newmode) != 0) - { - error (0, errno, _("cannot set permissions of `%s'"), - quote (argv[optind])); - exit_status = EXIT_FAILURE; - } + { + if (set_security_context) + defaultcon (argv[optind], S_IFIFO); + if (mkfifo (argv[optind], newmode) != 0) + { + error (0, errno, _("cannot create fifo %s"), quote (argv[optind])); + exit_status = EXIT_FAILURE; + } + else if (specified_mode && lchmod (argv[optind], newmode) != 0) + { + error (0, errno, _("cannot set permissions of `%s'"), + quote (argv[optind])); + exit_status = EXIT_FAILURE; + } + } exit (exit_status); } diff --git a/src/mknod.c b/src/mknod.c index c79468c6d..908d84034 100644 --- a/src/mknod.c +++ b/src/mknod.c @@ -26,6 +26,7 @@ #include "error.h" #include "modechange.h" #include "quote.h" +#include "selinux.h" #include "smack.h" #include "xstrtol.h" @@ -62,7 +63,8 @@ Create the special file NAME of the given TYPE.\n\ -m, --mode=MODE set file permission bits to MODE, not a=rw - umask\n\ "), stdout); fputs (_("\ - -Z, --context=CTX set the SELinux security context of NAME to CTX\n\ + -Z, --context[=CTX] set the SELinux security context of NAME to\n\ + default type, or to CTX if specified\n\ "), stdout); fputs (HELP_OPTION_DESCRIPTION, stdout); fputs (VERSION_OPTION_DESCRIPTION, stdout); @@ -94,6 +96,7 @@ main (int argc, char **argv) int expected_operands; mode_t node_type; security_context_t scontext = NULL; + bool set_security_context = false; initialize_main (&argc, &argv); set_program_name (argv[0]); @@ -103,7 +106,7 @@ main (int argc, char **argv) atexit (close_stdout); - while ((optc = getopt_long (argc, argv, "m:Z:", longopts, NULL)) != -1) + while ((optc = getopt_long (argc, argv, "m:Z", longopts, NULL)) != -1) { switch (optc) { @@ -111,7 +114,24 @@ main (int argc, char **argv) specified_mode = optarg; break; case 'Z': - scontext = optarg; + if (is_smack_enabled ()) + { + /* We don't yet support -Z to restore context with SMACK. */ + scontext = optarg; + } + else if (is_selinux_enabled () > 0) + { + if (optarg) + scontext = optarg; + else + set_security_context = true; + } + else if (optarg) + { + error (0, 0, + _("warning: ignoring --context; " + "it requires an SELinux/SMACK-enabled kernel")); + } break; case_GETOPT_HELP_CHAR; case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS); @@ -224,12 +244,17 @@ main (int argc, char **argv) error (EXIT_FAILURE, 0, _("invalid device %s %s"), s_major, s_minor); #endif + if (set_security_context) + defaultcon (argv[optind], node_type); + if (mknod (argv[optind], newmode | node_type, device) != 0) error (EXIT_FAILURE, errno, "%s", quote (argv[optind])); } break; case 'p': /* 'pipe' */ + if (set_security_context) + defaultcon (argv[optind], S_IFIFO); if (mkfifo (argv[optind], newmode) != 0) error (EXIT_FAILURE, errno, "%s", quote (argv[optind])); break; @@ -55,6 +55,7 @@ static bool remove_trailing_slashes; static struct option const long_options[] = { {"backup", optional_argument, NULL, 'b'}, + {"context", no_argument, NULL, 'Z'}, {"force", no_argument, NULL, 'f'}, {"interactive", no_argument, NULL, 'i'}, {"no-clobber", no_argument, NULL, 'n'}, @@ -120,6 +121,7 @@ cp_option_init (struct cp_options *x) x->preserve_timestamps = true; x->explicit_no_preserve_mode= false; x->preserve_security_context = selinux_enabled; + x->set_security_context = false; x->reduce_diagnostics = false; x->data_copy_required = true; x->require_preserve = false; /* FIXME: maybe make this an option */ @@ -316,6 +318,8 @@ If you specify more than one of -i, -f, -n, only the final one takes effect.\n\ than the destination file or when the\n\ destination file is missing\n\ -v, --verbose explain what is being done\n\ + -Z, --context set SELinux security context of destination\n\ + file to default type\n\ "), stdout); fputs (HELP_OPTION_DESCRIPTION, stdout); fputs (VERSION_OPTION_DESCRIPTION, stdout); @@ -350,6 +354,7 @@ main (int argc, char **argv) bool no_target_directory = false; int n_files; char **file; + bool selinux_enabled = (0 < is_selinux_enabled ()); initialize_main (&argc, &argv); set_program_name (argv[0]); @@ -368,7 +373,7 @@ main (int argc, char **argv) we'll actually use backup_suffix_string. */ backup_suffix_string = getenv ("SIMPLE_BACKUP_SUFFIX"); - while ((c = getopt_long (argc, argv, "bfint:uvS:T", long_options, NULL)) + while ((c = getopt_long (argc, argv, "bfint:uvS:TZ", long_options, NULL)) != -1) { switch (c) @@ -418,6 +423,14 @@ main (int argc, char **argv) make_backups = true; backup_suffix_string = optarg; break; + case 'Z': + /* politely decline if we're not on a selinux-enabled kernel. */ + if (selinux_enabled) + { + x.preserve_security_context = false; + x.set_security_context = true; + } + break; case_GETOPT_HELP_CHAR; case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS); default: diff --git a/src/runcon.c b/src/runcon.c index 8a0b34e95..2827992d9 100644 --- a/src/runcon.c +++ b/src/runcon.c @@ -85,7 +85,7 @@ Usage: %s CONTEXT COMMAND [args]\n\ or: %s [ -c ] [-u USER] [-r ROLE] [-t TYPE] [-l RANGE] COMMAND [args]\n\ "), program_name, program_name); fputs (_("\ -Run a program in a different security context.\n\ +Run a program in a different SELinux security context.\n\ With neither CONTEXT nor COMMAND, print the current security context.\n\ "), stdout); @@ -197,8 +197,8 @@ main (int argc, char **argv) } if (is_selinux_enabled () != 1) - error (EXIT_FAILURE, 0, - _("%s may be used only on a SELinux kernel"), program_name); + error (EXIT_FAILURE, 0, _("%s may be used only on a SELinux kernel"), + program_name); if (context) { @@ -223,8 +223,7 @@ main (int argc, char **argv) /* compute result of process transition */ if (security_compute_create (cur_context, file_context, SECCLASS_PROCESS, &new_context) != 0) - error (EXIT_FAILURE, errno, - _("failed to compute a new context")); + error (EXIT_FAILURE, errno, _("failed to compute a new context")); /* free contexts */ freecon (file_context); freecon (cur_context); diff --git a/src/system.h b/src/system.h index db8931635..6b242a816 100644 --- a/src/system.h +++ b/src/system.h @@ -330,7 +330,7 @@ enum #define GETOPT_VERSION_OPTION_DECL \ "version", no_argument, NULL, GETOPT_VERSION_CHAR #define GETOPT_SELINUX_CONTEXT_OPTION_DECL \ - "context", required_argument, NULL, 'Z' + "context", optional_argument, NULL, 'Z' #define case_GETOPT_HELP_CHAR \ case GETOPT_HELP_CHAR: \ |