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).
-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.
# 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)
#include <stdio.h>
#include <assert.h>
#include <sys/types.h>
+#include <selinux/selinux.h>
#if HAVE_HURD_H
# include <hurd.h>
{
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)
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
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
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.
}
}
+ if (x->preserve_security_context)
+ restore_default_fscreatecon_or_die ();
+
/* There's no need to preserve timestamps or permissions. */
preserve_metadata = false;
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),
/* 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
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
#include <stdio.h>
#include <sys/types.h>
#include <getopt.h>
+#include <selinux/selinux.h>
#include "system.h"
#include "argmatch.h"
/* 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;
-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\
x->preserve_links = false;
x->preserve_mode = false;
x->preserve_timestamps = false;
+ x->preserve_security_context = false;
x->require_preserve = false;
x->recursive = false;
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);
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:
atexit (close_stdout);
+ selinux_enabled = (0 < is_selinux_enabled ());
cp_option_init (&x);
/* FIXME: consider not calling getenv for SIMPLE_BACKUP_SUFFIX unless
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;
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 ();
#include <signal.h>
#include <pwd.h>
#include <grp.h>
+#include <selinux/selinux.h>
#include "system.h"
#include "backupfile.h"
#include "mkdir-p.h"
#include "modechange.h"
#include "quote.h"
+#include "quotearg.h"
#include "savewd.h"
#include "stat-time.h"
#include "utimens.h"
# 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
/* 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'},
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
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];
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)
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:
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);
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;
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.
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.
-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 (_("\
#include <getopt.h>
#include <sys/types.h>
#include <assert.h>
+#include <selinux/selinux.h>
#include "system.h"
#include "argmatch.h"
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;
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 */