summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJim Meyering <jim@meyering.net>2007-01-20 16:10:43 +0100
committerJim Meyering <jim@meyering.net>2007-03-29 21:37:06 +0200
commit85ddc626be5fd202f04aa8ced398b5119174b556 (patch)
treefdece904533e6747c797254cff27512a3e82cb3d
parent5a7fe9c0f8a3154d52d44265c73675b91441b209 (diff)
downloadcoreutils-85ddc626be5fd202f04aa8ced398b5119174b556.tar.xz
cp, mv, install: add SELinux support, but unlike with the Red Hat
patch, mv and cp do not provide the "-Z context" option. * src/copy.c: Include <selinux/selinux.h>. (restore_default_fscreatecon): New function. (copy_reg): Make cp --preserve=context work for existing destination. (copy_internal): Likewise for new destinations. * src/copy.h (cp_options) [preserve_security_context]: New member. * src/cp.c: Include <selinux/selinux.h>. (selinux_enabled): New global. (usage): Mention new --preserve=context option. (PRESERVE_CONTEXT): Define/use. (decode_preserve_arg): Handle PRESERVE_CONTEXT. (main): Remove an obsolete comment. If --preserve=context is specified on a system without SELinux enabled, give a diagnostic and fail. * src/mv.c: Include <selinux/selinux.h>. Set x->preserve_security_context if SELinux is enabled. * src/install.c: Accept new "-Z, --context=C" option. Accept --preserve-context option (but not -P option). Accept alternate spelling: --preserve_context, for now. Include <selinux/selinux.h> and "quotearg.h". (selinux_enabled, use_default_selinux_context): New globals. (PRESERVE_CONTEXT_OPTION): Define. (cp_option_init): Default: do not preserve security context. (setdefaultfilecon): New function. (main): Honor new options. * src/Makefile.am (mv_LDADD, cp_LDADD, ginstall_LDADD): Add $(LIB_SELINUX).
-rw-r--r--ChangeLog-selinux31
-rw-r--r--src/Makefile.am6
-rw-r--r--src/copy.c79
-rw-r--r--src/copy.h5
-rw-r--r--src/cp.c30
-rw-r--r--src/install.c103
-rw-r--r--src/mv.c4
7 files changed, 243 insertions, 15 deletions
diff --git a/ChangeLog-selinux b/ChangeLog-selinux
index 05200e364..de9cc2fae 100644
--- a/ChangeLog-selinux
+++ b/ChangeLog-selinux
@@ -1,4 +1,33 @@
-2007-01-13 Jim Meyering <jim@meyering.net>
+2007-01-20 Jim Meyering <jim@meyering.net>
+
+ cp, mv, install: add SELinux support, but unlike with the Red Hat
+ patch, mv and cp do not provide the "-Z context" option.
+ * src/copy.c: Include <selinux/selinux.h>.
+ (restore_default_fscreatecon): New function.
+ (copy_reg): Make cp --preserve=context work for existing destination.
+ (copy_internal): Likewise for new destinations.
+ * src/copy.h (cp_options) [preserve_security_context]: New member.
+ * src/cp.c: Include <selinux/selinux.h>.
+ (selinux_enabled): New global.
+ (usage): Mention new --preserve=context option.
+ (PRESERVE_CONTEXT): Define/use.
+ (decode_preserve_arg): Handle PRESERVE_CONTEXT.
+ (main): Remove an obsolete comment.
+ If --preserve=context is specified on a system without SELinux
+ enabled, give a diagnostic and fail.
+ * src/mv.c: Include <selinux/selinux.h>.
+ Set x->preserve_security_context if SELinux is enabled.
+ * src/install.c: Accept new "-Z, --context=C" option.
+ Accept --preserve-context option (but not -P option).
+ Accept alternate spelling: --preserve_context, for now.
+ Include <selinux/selinux.h> and "quotearg.h".
+ (selinux_enabled, use_default_selinux_context): New globals.
+ (PRESERVE_CONTEXT_OPTION): Define.
+ (cp_option_init): Default: do not preserve security context.
+ (setdefaultfilecon): New function.
+ (main): Honor new options.
+ * src/Makefile.am (mv_LDADD, cp_LDADD, ginstall_LDADD):
+ Add $(LIB_SELINUX).
* tests/misc/selinux [VERBOSE]: Print version info for each
of the tested tools, not just ls.
diff --git a/src/Makefile.am b/src/Makefile.am
index 3f65a1e37..c999c6e1b 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -61,9 +61,9 @@ LDADD = ../lib/libcoreutils.a $(LIBINTL) ../lib/libcoreutils.a
# for eaccess in lib/euidaccess.c.
chcon_LDADD = $(LDADD) $(LIB_SELINUX)
-cp_LDADD = $(LDADD) $(LIB_EACCESS)
-ginstall_LDADD = $(LDADD) $(LIB_EACCESS)
-mv_LDADD = $(LDADD) $(LIB_EACCESS)
+cp_LDADD = $(LDADD) $(LIB_EACCESS) $(LIB_SELINUX)
+ginstall_LDADD = $(LDADD) $(LIB_EACCESS) $(LIB_SELINUX)
+mv_LDADD = $(LDADD) $(LIB_EACCESS) $(LIB_SELINUX)
pathchk_LDADD = $(LDADD) $(LIB_EACCESS)
rm_LDADD = $(LDADD) $(LIB_EACCESS)
test_LDADD = $(LDADD) $(LIB_EACCESS)
diff --git a/src/copy.c b/src/copy.c
index 786de2f9e..f60fa55bd 100644
--- a/src/copy.c
+++ b/src/copy.c
@@ -21,6 +21,7 @@
#include <stdio.h>
#include <assert.h>
#include <sys/types.h>
+#include <selinux/selinux.h>
#if HAVE_HURD_H
# include <hurd.h>
@@ -298,6 +299,36 @@ copy_reg (char const *src_name, char const *dst_name,
{
dest_desc = open (dst_name, O_WRONLY | O_TRUNC | O_BINARY);
+ /* 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)
+ {
+ security_context_t con;
+ if (getfscreatecon (&con) < 0)
+ {
+ error (0, errno, _("failed to get file system create context"));
+ return_val = false;
+ goto close_src_desc;
+ }
+
+ if (con)
+ {
+ if (fsetfilecon (dest_desc, con) < 0)
+ {
+ error (0, errno,
+ _("failed to set the security context of %s to %s"),
+ quote_n (0, dst_name), quote_n (1, con));
+ return_val = false;
+ freecon (con);
+ goto close_src_desc;
+ }
+ freecon(con);
+ }
+ }
+
if (dest_desc < 0 && x->unlink_dest_after_failed_open)
{
if (unlink (dst_name) != 0)
@@ -1001,6 +1032,15 @@ emit_verbose (char const *src, char const *dst, char const *backup_dst_name)
putchar ('\n');
}
+/* A wrapper around "setfscreatecon (NULL)" that exits upon failure. */
+static void
+restore_default_fscreatecon_or_die (void)
+{
+ if (setfscreatecon (NULL) != 0)
+ error (EXIT_FAILURE, errno,
+ _("failed to restore the default file creation context"));
+}
+
/* Copy the file SRC_NAME to the file DST_NAME. The files may be of
any type. NEW_DST should be true if the file DST_NAME cannot
exist because its parent directory was just created; NEW_DST should
@@ -1349,7 +1389,7 @@ copy_internal (char const *src_name, char const *dst_name,
if (x->move_mode && src_sb.st_nlink == 1)
{
- earlier_file = src_to_dest_lookup (src_sb.st_ino, src_sb.st_dev);
+ earlier_file = src_to_dest_lookup (src_sb.st_ino, src_sb.st_dev);
}
else if ((x->preserve_links
&& (1 < src_sb.st_nlink
@@ -1539,6 +1579,37 @@ copy_internal (char const *src_name, char const *dst_name,
delayed_ok = true;
+ if (x->preserve_security_context)
+ {
+ security_context_t con;
+
+ if (0 <= lgetfilecon (src_name, &con))
+ {
+ if (setfscreatecon (con) < 0)
+ {
+ error (0, errno,
+ _("failed to set default file creation context to %s"),
+ quote (con));
+ if (x->require_preserve)
+ {
+ freecon (con);
+ return false;
+ }
+ }
+ freecon (con);
+ }
+ else
+ {
+ if (errno != ENOTSUP && errno != ENODATA)
+ {
+ error (0, errno,
+ _("failed to get security context of %s"),
+ quote (src_name));
+ return false;
+ }
+ }
+ }
+
/* In certain modes (cp's --symbolic-link), and for certain file types
(symlinks and hard links) it doesn't make sense to preserve metadata,
or it's possible to preserve only some of it.
@@ -1768,6 +1839,9 @@ copy_internal (char const *src_name, char const *dst_name,
}
}
+ if (x->preserve_security_context)
+ restore_default_fscreatecon_or_die ();
+
/* There's no need to preserve timestamps or permissions. */
preserve_metadata = false;
@@ -1901,6 +1975,9 @@ copy_internal (char const *src_name, char const *dst_name,
un_backup:
+ if (x->preserve_security_context)
+ restore_default_fscreatecon_or_die ();
+
/* We have failed to create the destination file.
If we've just added a dev/ino entry via the remember_copied
call above (i.e., unless we've just failed to create a hard link),
diff --git a/src/copy.h b/src/copy.h
index c815baf64..eab6c8678 100644
--- a/src/copy.h
+++ b/src/copy.h
@@ -1,5 +1,5 @@
/* core functions for copying files and directories
- Copyright (C) 89, 90, 91, 1995-2005 Free Software Foundation.
+ Copyright (C) 89, 90, 91, 1995-2007 Free Software Foundation.
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
@@ -127,6 +127,9 @@ struct cp_options
bool preserve_ownership;
bool preserve_mode;
bool preserve_timestamps;
+ /* If true, attempt to preserve the SELinux security context, too.
+ Set this only if the kernel is SELinux enabled. */
+ bool preserve_security_context;
/* Enabled for mv, and for cp by the --preserve=links option.
If true, attempt to preserve in the destination files any
diff --git a/src/cp.c b/src/cp.c
index 1fffbd794..c63e047d9 100644
--- a/src/cp.c
+++ b/src/cp.c
@@ -21,6 +21,7 @@
#include <stdio.h>
#include <sys/types.h>
#include <getopt.h>
+#include <selinux/selinux.h>
#include "system.h"
#include "argmatch.h"
@@ -85,6 +86,9 @@ enum
/* The invocation name of this program. */
char *program_name;
+/* True if the kernel is SELinux enabled. */
+static bool selinux_enabled;
+
/* If true, the command "cp x/e_file e_dir" uses "e_dir/x/e_file"
as its destination instead of the usual "e_dir/e_file." */
static bool parents_option = false;
@@ -191,7 +195,7 @@ Mandatory arguments to long options are mandatory for short options too.\n\
-p same as --preserve=mode,ownership,timestamps\n\
--preserve[=ATTR_LIST] preserve the specified attributes (default:\n\
mode,ownership,timestamps), if possible\n\
- additional attributes: links, all\n\
+ additional attributes: context, links, all\n\
"), stdout);
fputs (_("\
--no-preserve=ATTR_LIST don't preserve the specified attributes\n\
@@ -749,6 +753,7 @@ cp_option_init (struct cp_options *x)
x->preserve_links = false;
x->preserve_mode = false;
x->preserve_timestamps = false;
+ x->preserve_security_context = false;
x->require_preserve = false;
x->recursive = false;
@@ -777,18 +782,19 @@ decode_preserve_arg (char const *arg, struct cp_options *x, bool on_off)
PRESERVE_TIMESTAMPS,
PRESERVE_OWNERSHIP,
PRESERVE_LINK,
+ PRESERVE_CONTEXT,
PRESERVE_ALL
};
static enum File_attribute const preserve_vals[] =
{
PRESERVE_MODE, PRESERVE_TIMESTAMPS,
- PRESERVE_OWNERSHIP, PRESERVE_LINK, PRESERVE_ALL
+ PRESERVE_OWNERSHIP, PRESERVE_LINK, PRESERVE_CONTEXT, PRESERVE_ALL
};
/* Valid arguments to the `--preserve' option. */
static char const* const preserve_args[] =
{
"mode", "timestamps",
- "ownership", "links", "all", NULL
+ "ownership", "links", "context", "all", NULL
};
ARGMATCH_VERIFY (preserve_args, preserve_vals);
@@ -824,11 +830,17 @@ decode_preserve_arg (char const *arg, struct cp_options *x, bool on_off)
x->preserve_links = on_off;
break;
+ case PRESERVE_CONTEXT:
+ x->preserve_security_context = on_off;
+ break;
+
case PRESERVE_ALL:
x->preserve_mode = on_off;
x->preserve_timestamps = on_off;
x->preserve_ownership = on_off;
x->preserve_links = on_off;
+ if (selinux_enabled)
+ x->preserve_security_context = on_off;
break;
default:
@@ -862,6 +874,7 @@ main (int argc, char **argv)
atexit (close_stdout);
+ selinux_enabled = (0 < is_selinux_enabled ());
cp_option_init (&x);
/* FIXME: consider not calling getenv for SIMPLE_BACKUP_SUFFIX unless
@@ -1048,9 +1061,6 @@ main (int argc, char **argv)
x.dereference = DEREF_ALWAYS;
}
- /* The key difference between -d (--no-dereference) and not is the version
- of `stat' to call. */
-
if (x.recursive)
x.copy_as_regular = copy_contents;
@@ -1059,6 +1069,14 @@ 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"));
+ }
+
/* Allocate space for remembering copied and created files. */
hash_init ();
diff --git a/src/install.c b/src/install.c
index 6f85a24f4..f6152f355 100644
--- a/src/install.c
+++ b/src/install.c
@@ -24,6 +24,7 @@
#include <signal.h>
#include <pwd.h>
#include <grp.h>
+#include <selinux/selinux.h>
#include "system.h"
#include "backupfile.h"
@@ -35,6 +36,7 @@
#include "mkdir-p.h"
#include "modechange.h"
#include "quote.h"
+#include "quotearg.h"
#include "savewd.h"
#include "stat-time.h"
#include "utimens.h"
@@ -49,6 +51,9 @@
# include <sys/wait.h>
#endif
+static int selinux_enabled = 0;
+static bool use_default_selinux_context = true;
+
#if ! HAVE_ENDGRENT
# define endgrent() ((void) 0)
#endif
@@ -121,15 +126,28 @@ static bool strip_files;
/* If true, install a directory instead of a regular file. */
static bool dir_arg;
+/* For long options that have no equivalent short option, use a
+ non-character as a pseudo short option, starting with CHAR_MAX + 1. */
+enum
+{
+ PRESERVE_CONTEXT_OPTION = CHAR_MAX + 1
+};
+
static struct option const long_options[] =
{
{"backup", optional_argument, NULL, 'b'},
+ {GETOPT_SELINUX_CONTEXT_OPTION_DECL},
{"directory", no_argument, NULL, 'd'},
{"group", required_argument, NULL, 'g'},
{"mode", required_argument, NULL, 'm'},
{"no-target-directory", no_argument, NULL, 'T'},
{"owner", required_argument, NULL, 'o'},
{"preserve-timestamps", no_argument, NULL, 'p'},
+ {"preserve-context", no_argument, NULL, PRESERVE_CONTEXT_OPTION},
+ /* Continue silent support for --preserve_context until Jan 2008. FIXME-obs
+ After that, FIXME-obs: warn in, say, late 2008, and disable altogether
+ a year or two later. */
+ {"preserve_context", no_argument, NULL, PRESERVE_CONTEXT_OPTION},
{"strip", no_argument, NULL, 's'},
{"suffix", required_argument, NULL, 'S'},
{"target-directory", required_argument, NULL, 't'},
@@ -169,11 +187,47 @@ cp_option_init (struct cp_options *x)
x->stdin_tty = false;
x->update = false;
+ x->preserve_security_context = false;
x->verbose = false;
x->dest_info = NULL;
x->src_info = NULL;
}
+/* Modify file context to match the specified policy.
+ If an error occurs the file will remain with the default directory
+ context. */
+static void
+setdefaultfilecon (char const *file)
+{
+ struct stat st;
+ security_context_t scontext = NULL;
+ if (selinux_enabled != 1)
+ {
+ /* Indicate no context found. */
+ return;
+ }
+ if (lstat (file, &st) != 0)
+ return;
+
+ /* If there's an error determining the context, or it has none,
+ return to allow default context */
+ if ((matchpathcon (file, st.st_mode, &scontext) != 0) ||
+ (strcmp (scontext, "<<none>>") == 0))
+ {
+ if (scontext != NULL)
+ freecon (scontext);
+ return;
+ }
+
+ if (lsetfilecon (file, scontext) < 0 && errno != ENOTSUP)
+ error (0, errno,
+ _("warning: %s: failed to change context to %s"),
+ quotearg_colon (file), scontext);
+
+ freecon (scontext);
+ return;
+}
+
/* FILE is the last operand of this command. Return true if FILE is a
directory. But report an error there is a problem accessing FILE,
or if FILE does not exist but would have to refer to an existing
@@ -222,6 +276,9 @@ main (int argc, char **argv)
bool no_target_directory = false;
int n_files;
char **file;
+ security_context_t scontext = NULL;
+ /* set iff kernel has extra selinux system calls */
+ selinux_enabled = (0 < is_selinux_enabled ());
initialize_main (&argc, &argv);
program_name = argv[0];
@@ -243,7 +300,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, "bcsDdg:m:o:pt:TvS:", long_options,
+ while ((optc = getopt_long (argc, argv, "bcsDdg:m:o:pt:TvS:Z:", long_options,
NULL)) != -1)
{
switch (optc)
@@ -305,6 +362,27 @@ main (int argc, char **argv)
case 'T':
no_target_directory = true;
break;
+
+ case PRESERVE_CONTEXT_OPTION:
+ if ( ! selinux_enabled)
+ {
+ error (0, 0, _("Warning: ignoring --preserve-context; "
+ "this kernel is not SELinux-enabled."));
+ break;
+ }
+ x.preserve_security_context = true;
+ use_default_selinux_context = false;
+ break;
+ case 'Z':
+ if ( ! selinux_enabled)
+ {
+ error (0, 0, _("Warning: ignoring --context (-Z); "
+ "this kernel is not SELinux-enabled."));
+ break;
+ }
+ scontext = optarg;
+ use_default_selinux_context = false;
+ break;
case_GETOPT_HELP_CHAR;
case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
default:
@@ -320,6 +398,11 @@ 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);
@@ -328,6 +411,11 @@ main (int argc, char **argv)
version_control_string)
: no_backups);
+ if (scontext && setfscreatecon (scontext) < 0)
+ error (EXIT_FAILURE, errno,
+ _("failed to set default file creation context to %s"),
+ quote (scontext));
+
n_files = argc - optind;
file = argv + optind;
@@ -503,6 +591,7 @@ copy_file (const char *from, const char *to, const struct cp_options *x)
static bool
change_attributes (char const *name)
{
+ bool ok = false;
/* chown must precede chmod because on some systems,
chown clears the set[ug]id bits for non-superusers,
resulting in incorrect permissions.
@@ -521,9 +610,12 @@ change_attributes (char const *name)
else if (chmod (name, mode) != 0)
error (0, errno, _("cannot change permissions of %s"), quote (name));
else
- return true;
+ ok = true;
+
+ if (use_default_selinux_context)
+ setdefaultfilecon (name);
- return false;
+ return ok;
}
/* Set the timestamps of file TO to match those of file FROM.
@@ -687,6 +779,11 @@ Mandatory arguments to long options are mandatory for short options too.\n\
-T, --no-target-directory treat DEST as a normal file\n\
-v, --verbose print the name of each directory as it is created\n\
"), stdout);
+ fputs (_("\
+ --preserve-context preserve SELinux security context\n\
+ -Z, --context=CONTEXT set SELinux security context of files and directories\n\
+"), stdout);
+
fputs (HELP_OPTION_DESCRIPTION, stdout);
fputs (VERSION_OPTION_DESCRIPTION, stdout);
fputs (_("\
diff --git a/src/mv.c b/src/mv.c
index 2ca60d08e..90387f79b 100644
--- a/src/mv.c
+++ b/src/mv.c
@@ -22,6 +22,7 @@
#include <getopt.h>
#include <sys/types.h>
#include <assert.h>
+#include <selinux/selinux.h>
#include "system.h"
#include "argmatch.h"
@@ -113,6 +114,8 @@ rm_option_init (struct rm_options *x)
static void
cp_option_init (struct cp_options *x)
{
+ bool selinux_enabled = (0 < is_selinux_enabled ());
+
x->copy_as_regular = false; /* FIXME: maybe make this an option */
x->dereference = DEREF_NEVER;
x->unlink_dest_before_opening = false;
@@ -126,6 +129,7 @@ cp_option_init (struct cp_options *x)
x->preserve_links = true;
x->preserve_mode = true;
x->preserve_timestamps = true;
+ x->preserve_security_context = selinux_enabled;
x->require_preserve = false; /* FIXME: maybe make this an option */
x->recursive = true;
x->sparse_mode = SPARSE_AUTO; /* FIXME: maybe make this an option */