From 7958a4a4fe234f9787daf178a60bc83449605dac Mon Sep 17 00:00:00 2001 From: Pádraig Brady Date: Wed, 27 Nov 2013 12:26:51 +0000 Subject: 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. --- NEWS | 13 ++- doc/coreutils.texi | 64 ++++++------- src/chcon.c | 2 +- src/copy.c | 222 ++++++++++++++++++++++++++++++++-------------- src/copy.h | 3 + src/cp.c | 64 ++++++++++--- src/id.c | 6 +- src/install.c | 53 +++++++---- src/local.mk | 16 +++- src/mkdir.c | 86 +++++++++++++++--- src/mkfifo.c | 52 ++++++++--- src/mknod.c | 31 ++++++- src/mv.c | 15 +++- src/runcon.c | 9 +- src/system.h | 2 +- tests/cp/cp-a-selinux.sh | 76 +++++++++++++++- tests/local.mk | 1 + tests/mkdir/restorecon.sh | 72 +++++++++++++++ tests/mkdir/selinux.sh | 2 +- 19 files changed, 612 insertions(+), 177 deletions(-) create mode 100755 tests/mkdir/restorecon.sh diff --git a/NEWS b/NEWS index f9f3a9e8d..ecc094a7b 100644 --- a/NEWS +++ b/NEWS @@ -52,6 +52,13 @@ GNU coreutils NEWS -*- outline -*- ** New features + cp, install, mkdir, mknod, mkfifo and mv now support "restorecon" + functionality through the -Z option, to set the SELinux context + appropriate for the new item location in the file system. + + csplit accepts a new option: --suppressed-matched, to elide the lines + used to identify the split points. + df --output now accepts a 'file' field, to propagate a specified command line argument through to the output. @@ -73,9 +80,6 @@ GNU coreutils NEWS -*- outline -*- uniq accepts a new option: --group to print all items, while separating unique groups with empty lines. - csplit accepts a new option: --suppressed-matched, to elide the lines - used to identify the split points. - shred accepts new parameters to the --remove option to give greater control over that operation, which can greatly reduce sync overhead. @@ -89,6 +93,9 @@ GNU coreutils NEWS -*- outline -*- Previously, it would create a hard link of the symbolic link, even when the dereferencing options -L or -H were specified. + cp, install, mkdir, mknod and mkfifo no longer accept an argument to the + short -Z option. The --context equivalent still takes an optional argument. + dd status=none now suppresses all non fatal diagnostic messages, not just the transfer counts. diff --git a/doc/coreutils.texi b/doc/coreutils.texi index 7a5023101..c3237304d 100644 --- a/doc/coreutils.texi +++ b/doc/coreutils.texi @@ -8467,6 +8467,24 @@ Skip subdirectories that are on different file systems from the one that the copy started on. However, mount point directories @emph{are} copied. +@macro optContext +@item -Z +@itemx --context[=@var{context}] +@opindex -Z +@opindex --context +@cindex SELinux, setting/restoring security context +@cindex security context +Without a specified @var{context}, adjust the SELinux security context according +to the system default type for destination files, similarly to the +@command{restorecon} command. +The long form of this option with a specific context specified, +will set the context for newly created files only. +With a specified context, if SELinux is disabled, a warning is issued. +@end macro +@optContext +This option is mutually exclusive with the @option{--preserve=context} +option, and overrides the @option{--preserve=all} and @option{-a} options. + @end table @exitstatus @@ -9110,15 +9128,9 @@ Program used to strip binaries. @opindex --verbose Print the name of each file before copying it. -@item -Z @var{context} -@itemx --context=@var{context} -@opindex -Z -@opindex --context -@cindex SELinux -@cindex security context -Set the default SELinux security context to be used for any -created files and directories. If SELinux is disabled then -print a warning and ignore the option. +@optContext +This option is mutually exclusive with the @option{--preserve-context} option. + @end table @@ -9251,6 +9263,16 @@ Print the name of each file before moving it. @optNoTargetDirectory +@item -Z +@itemx --context +@opindex -Z +@opindex --context +@cindex SELinux, restoring security context +@cindex security context +This option functions similarly to the @command{restorecon} command, +by adjusting the SELinux security context according +to the system default type for destination files. + @end table @exitstatus @@ -10066,13 +10088,7 @@ newly-created parent directories are inherited. Print a message for each created directory. This is most useful with @option{--parents}. -@item -Z @var{context} -@itemx --context=@var{context} -@opindex -Z -@opindex --context -@cindex SELinux -@cindex security context -Set the default SELinux security context to be used for created directories. +@optContext @end table @@ -10113,13 +10129,7 @@ Set the mode of created FIFOs to @var{mode}, which is symbolic as in for the point of departure. @var{mode} should specify only file permission bits. @xref{File permissions}. -@item -Z @var{context} -@itemx --context=@var{context} -@opindex -Z -@opindex --context -@cindex SELinux -@cindex security context -Set the default SELinux security context to be used for created FIFOs. +@optContext @end table @@ -10196,13 +10206,7 @@ Set the mode of created files to @var{mode}, which is symbolic as in @var{mode} should specify only file permission bits. @xref{File permissions}. -@item -Z @var{context} -@itemx --context=@var{context} -@opindex -Z -@opindex --context -@cindex SELinux -@cindex security context -Set the default SELinux security context to be used for created files. +@optContext @end table 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 @@ -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 diff --git a/src/cp.c b/src/cp.c index 78c0a0402..59d659d03 100644 --- a/src/cp.c +++ b/src/cp.c @@ -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} @@ -227,6 +228,10 @@ Copy SOURCE to DEST, or multiple SOURCE(s) to DIRECTORY.\n\ destination file is missing\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); @@ -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) diff --git a/src/id.c b/src/id.c index bae9c181a..884fd16db 100644 --- a/src/id.c +++ b/src/id.c @@ -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 "<>" check + is only needed for libselinux < 1.20 (2005-01-04). */ if ((matchpathcon (file, st.st_mode, &scontext) != 0) || STREQ (scontext, "<>")) { @@ -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; diff --git a/src/mv.c b/src/mv.c index 1cfcd82f7..cb6b70e25 100644 --- a/src/mv.c +++ b/src/mv.c @@ -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: \ diff --git a/tests/cp/cp-a-selinux.sh b/tests/cp/cp-a-selinux.sh index e7bdcf1df..ac7bf1c3a 100755 --- a/tests/cp/cp-a-selinux.sh +++ b/tests/cp/cp-a-selinux.sh @@ -1,5 +1,5 @@ #!/bin/sh -# Ensure that cp -a and cp --preserve=context work properly. +# Ensure that cp -Z, -a and cp --preserve=context work properly. # In particular, test on a writable NFS partition. # Check also locally if --preserve=context, -a and --preserve=all # does work @@ -41,6 +41,45 @@ test -s err && fail=1 #there must be no stderr output for -a ls -Z e | grep $ctx || fail=1 ls -Z f | grep $ctx || fail=1 +# Check restorecon (-Z) functionality for file and directory +get_selinux_type() { ls -Zd "$1" | sed -n 's/.*:\(.*_t\):.*/\1/p'; } +# Also make a dir with our known context +mkdir c_d || framework_failure_ +chcon $ctx c_d || framework_failure_ +# Get the type of this known context for file and dir +old_type_f=$(get_selinux_type c) +old_type_d=$(get_selinux_type c_d) +# Setup copies for manipulation with restorecon +# and get the adjusted type for comparison +cp -a c Z1 || fail=1 +cp -a c_d Z1_d || fail=1 +if restorecon Z1 Z1_d 2>/dev/null; then + new_type_f=$(get_selinux_type Z1) + new_type_d=$(get_selinux_type Z1_d) + + # Ensure -Z sets the type like restorecon does + cp -Z c Z2 || fail=1 + cpZ_type_f=$(get_selinux_type Z2) + test "$cpZ_type_f" = "$new_type_f" || fail=1 + + # Ensuze -Z overrides -a and that dirs are handled too + cp -aZ c Z3 || fail=1 + cp -aZ c_d Z3_d || fail=1 + cpaZ_type_f=$(get_selinux_type Z3) + cpaZ_type_d=$(get_selinux_type Z3_d) + test "$cpaZ_type_f" = "$new_type_f" || fail=1 + test "$cpaZ_type_d" = "$new_type_d" || fail=1 + + # Ensure -Z sets the type for existing files + mkdir -p existing/c_d || framework_failure_ + touch existing/c || framework_failure_ + cp -aZ c c_d existing || fail=1 + cpaZ_type_f=$(get_selinux_type existing/c) + cpaZ_type_d=$(get_selinux_type existing/c_d) + test "$cpaZ_type_f" = "$new_type_f" || fail=1 + test "$cpaZ_type_d" = "$new_type_d" || fail=1 +fi + skip=0 # Create a file system, then mount it with the context=... option. dd if=/dev/zero of=blob bs=8192 count=200 || skip=1 @@ -97,7 +136,7 @@ echo > g cp --preserve=context f g 2> out && fail=1 # Here, we *do* expect the destination to be empty. test -s g && fail=1 -sed "s/ .g' to .*//" out > k +sed "s/ .g'.*//" out > k mv k out compare exp out || fail=1 @@ -107,8 +146,39 @@ echo > g cp -a --preserve=context f g 2> out2 && fail=1 # Here, we *do* expect the destination to be empty. test -s g && fail=1 -sed "s/ .g' to .*//" out2 > k +sed "s/ .g'.*//" out2 > k mv k out2 compare exp out2 || fail=1 +for no_g_cmd in '' 'rm -f g'; do + # restorecon equivalent. Note even though the context + # returned from matchpathcon() will not match $ctx + # the resulting ENOTSUP warning will be suppressed. + # With absolute path + $no_g_cmd + cp -Z f $(realpath g) || fail=1 + # With relative path + $no_g_cmd + cp -Z f g || fail=1 + # -Z overrides -a + $no_g_cmd + cp -Z -a f g || fail=1 + # -Z doesn't take an arg + $no_g_cmd + cp -Z "$ctx" f g && fail=1 + + # Explicit context + $no_g_cmd + # Explicitly defaulting to the global $ctx should work + cp --context="$ctx" f g || fail=1 + # --context overrides -a + $no_g_cmd + cp -a --context="$ctx" f g || fail=1 +done + +# Mutually exlusive options +cp -Z --preserve=context f g && fail=1 +cp --preserve=context -Z f g && fail=1 +cp --preserve=context --context="$ctx" f g && fail=1 + Exit $fail diff --git a/tests/local.mk b/tests/local.mk index 2dd7e2063..19bbbd7d2 100644 --- a/tests/local.mk +++ b/tests/local.mk @@ -567,6 +567,7 @@ all_tests = \ tests/mkdir/parents.sh \ tests/mkdir/perm.sh \ tests/mkdir/selinux.sh \ + tests/mkdir/restorecon.sh \ tests/mkdir/special-1.sh \ tests/mkdir/t-slash.sh \ tests/mv/acl.sh \ diff --git a/tests/mkdir/restorecon.sh b/tests/mkdir/restorecon.sh new file mode 100755 index 000000000..01a647736 --- /dev/null +++ b/tests/mkdir/restorecon.sh @@ -0,0 +1,72 @@ +#!/bin/sh +# test mkdir, mknod, mkfifo -Z + +# Copyright (C) 2013 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 3 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, see . + +. "${srcdir=.}/tests/init.sh"; path_prepend_ ./src +print_ver_ mkdir +require_selinux_ + + +get_selinux_type() { ls -Zd "$1" | sed -n 's/.*:\(.*_t\):.*/\1/p'; } + +mkdir subdir || framework_failure_ +chcon 'root:object_r:tmp_t:s0' subdir || framework_failure_ +cd subdir + +# --- mkdir -Z --- +# Since in a tmp_t dir, dirs can be created as user_tmp_t ... +mkdir standard || framework_failure_ +mkdir restored || framework_failure_ +if restorecon restored 2>/dev/null; then + # ... but when restored can be set to user_home_t + # So ensure the type for these mkdir -Z cases matches + # the directory type as set by restorecon. + mkdir -Z single || fail=1 + # Run these as separate processes in case global context + # set for an arg, impacts on another arg + for dir in single_p single_p/existing multi/ple; do + mkdir -Zp "$dir" || fail=1 + done + restored_type=$(get_selinux_type 'restored') + test "$(get_selinux_type 'single')" = "$restored_type" || fail=1 + test "$(get_selinux_type 'single_p')" = "$restored_type" || fail=1 + test "$(get_selinux_type 'single_p/existing')" = "$restored_type" || fail=1 + test "$(get_selinux_type 'multi')" = "$restored_type" || fail=1 + test "$(get_selinux_type 'multi/ple')" = "$restored_type" || fail=1 +fi +if test "$fail" = '1'; then + ls -UZd standard restored + ls -UZd single single_p single_p/existing multi multi/ple +fi + +# --- mknod -Z and mkfifo -Z --- +# Assume if selinux present that we can create fifos +for cmd_w_arg in 'mknod' 'mkfifo'; do + # In OpenBSD's /bin/sh, mknod is a shell built-in. + # Running via "env" ensures we run our program and not the built-in. + basename="$cmd_w_arg" + test "$basename" = 'mknod' && nt='p' || nt='' + env -- $cmd_w_arg $basename $nt || fail=1 + env -- $cmd_w_arg ${basename}_restore $nt || fail=1 + if restorecon ${basename}_restore 2>/dev/null; then + env -- $cmd_w_arg -Z ${basename}_Z $nt || fail=1 + restored_type=$(get_selinux_type "${basename}_restore") + test "$(get_selinux_type ${basename}_Z)" = "$restored_type" || fail=1 + fi +done + +Exit $fail diff --git a/tests/mkdir/selinux.sh b/tests/mkdir/selinux.sh index e68f77b87..c1cfaf05e 100755 --- a/tests/mkdir/selinux.sh +++ b/tests/mkdir/selinux.sh @@ -32,7 +32,7 @@ msg="failed to set default file creation context to '$c':" for cmd_w_arg in 'mkdir dir' 'mknod b p' 'mkfifo f'; do # In OpenBSD's /bin/sh, mknod is a shell built-in. # Running via "env" ensures we run our program and not the built-in. - env -- $cmd_w_arg -Z $c 2> out && fail=1 + env -- $cmd_w_arg --context=$c 2> out && fail=1 set $cmd_w_arg; cmd=$1 echo "$cmd: $msg" > exp || fail=1 -- cgit v1.2.3-70-g09d2