]> git.ipfire.org Git - thirdparty/coreutils.git/commitdiff
cp, mv, install: add SELinux support, but unlike with the Red Hat
authorJim Meyering <jim@meyering.net>
Sat, 20 Jan 2007 15:10:43 +0000 (16:10 +0100)
committerJim Meyering <jim@meyering.net>
Sun, 18 Mar 2007 17:15:06 +0000 (18:15 +0100)
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).

ChangeLog-selinux
src/Makefile.am
src/copy.c
src/copy.h
src/cp.c
src/install.c
src/mv.c

index 05200e3648fc255af134d86eaa8a8f9787e3b041..de9cc2faea213fc0081f8284983259fb11d52a44 100644 (file)
@@ -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.
index a8ce95cd9d52034915586c34062ba72beb843d73..7ad9815e97c71670af24965aaf085ce45616097d 100644 (file)
@@ -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)
index 4bdb75cbb9707b2b2667a520f08ac2e13e7aa034..5283a6472b38d702e00507b20fa3e0a926da6342 100644 (file)
@@ -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>
@@ -302,6 +303,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)
@@ -995,6 +1026,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
@@ -1343,7 +1383,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
@@ -1533,6 +1573,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.
@@ -1762,6 +1833,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;
 
@@ -1895,6 +1969,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),
index c815baf64f8c0f0bb11ee2000f293c6e450e8e97..eab6c8678c00240a3f15c738b76f438a2629af19 100644 (file)
@@ -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
index 5759e0d07f2fd9278ce25d6b3a19662581b73b95..c5d3d32622fa449b550b70fc097236fc6d61d229 100644 (file)
--- 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 ();
index 04577518ceb133b305ad0214e85e7471b008222f..1613144bd38b2fd294688aff14460d2146ead608 100644 (file)
@@ -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 (_("\
index 1d1dddab89c6c8ec2d206ca95c7b888ba29814e0..6caf6423dc956bd292d3a47119b1c67b8bfa9946 100644 (file)
--- 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 */