-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

For some reason I could not get git to take more then a one line comment.
This is the comment I was trying to add.


        build: Change -Z opt w/out arg label target with default label

        The -Z without argument will ask SELinux what the default label of the i
        dest object should be and then attempt to change it.

        The -Z with an optional context will create all targets with the
        optional label. This is still supported for backwards compatability.

        The mv command does not support the optional argument.


BTW, it looks like coreutils has removed -ZCONTEXT from cp, if I had my
druthers, I would make all commands just use -Z and not allow the optional
arg.  Then -Z would just set the default label.  I would bet that no one in
history has executed a command like:

mkdir -Zunconfined_u:object_r:httpd_user_content_t:s0 ~/myweb


-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.12 (GNU/Linux)
Comment: Using GnuPG with Mozilla - http://www.enigmail.net/

iEYEARECAAYFAlCP568ACgkQrlYvE4MpobOSDgCfZE0KRKJobvMW4Fb8i7j6Hfd9
fHIAoID4PBCyoqc0omlu07kst5ouAjJp
=Hc8s
-----END PGP SIGNATURE-----
>From aa0c25ceeda99cb9df0e089aa927a4ac382c67bf Mon Sep 17 00:00:00 2001
From: rhatdan <[email protected]>
Date: Tue, 30 Oct 2012 09:48:15 -0400
Subject: [PATCH] build: Change -Z opt w/out arg label target with default
 label

---
 src/chcon.c   |   2 +-
 src/copy.c    |  52 +++++-------
 src/copy.h    |   3 +
 src/cp.c      |  35 ++++++--
 src/install.c |  16 ++--
 src/local.mk  |  15 +++-
 src/mkdir.c   |  19 ++++-
 src/mkfifo.c  |  13 ++-
 src/mknod.c   |  14 ++-
 src/mv.c      |  16 +++-
 src/runcon.c  |   2 +-
 src/selinux.c | 269 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 src/selinux.h |  26 ++++++
 13 files changed, 425 insertions(+), 57 deletions(-)
 create mode 100644 src/selinux.c
 create mode 100644 src/selinux.h

diff --git a/src/chcon.c b/src/chcon.c
index 34e92e4..0cf8fa6 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\
 \n\
 "), stdout);
diff --git a/src/copy.c b/src/copy.c
index 16aed03..c91a756 100644
--- a/src/copy.c
+++ b/src/copy.c
@@ -60,6 +60,7 @@
 #include "write-any-file.h"
 #include "areadlink.h"
 #include "yesno.h"
+#include "selinux.h"
 
 #if USE_XATTR
 # include <attr/error_context.h>
@@ -837,41 +838,18 @@ copy_reg (char const *src_name, char const *dst_name,
          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)
+      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 (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 (restorecon(dst_name, 0, x->preserve_security_context) < 0)  {
+            if (all_errors || (some_errors && !errno_unsupported (errno)))
+              error (0, errno, _("failed to set file system context on %s"), quote_n (0, dst_name));
+            return_val = false;
+            goto close_src_and_dst_desc;
+          }
         }
 
       if (dest_desc < 0 && x->unlink_dest_after_failed_open)
@@ -892,6 +870,9 @@ copy_reg (char const *src_name, char const *dst_name,
 
   if (*new_dst)
     {
+      if (x->set_security_context && (! x->require_preserve_context))
+        defaultcon(dst_name, dst_mode);
+
     open_with_O_CREAT:;
 
       int open_flags = O_WRONLY | O_CREAT | O_BINARY;
@@ -974,6 +955,9 @@ copy_reg (char const *src_name, char const *dst_name,
       goto close_src_and_dst_desc;
     }
 
+  if (x->set_security_context && ! x->preserve_security_context)
+    restorecon(dst_name, 1, false);
+
   /* --attributes-only overrides --reflink.  */
   if (data_copy_required && x->reflink_mode)
     {
@@ -2092,6 +2076,9 @@ 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)
+            restorecon(dst_name, 1, false);
+
           if (rename_succeeded)
             *rename_succeeded = true;
 
@@ -2231,6 +2218,11 @@ copy_internal (char const *src_name, char const *dst_name,
             return false;
         }
     }
+  else
+  {
+    if (x->set_security_context)
+      restorecon(dst_name, 1, false);
+  }
 
   if (S_ISDIR (src_mode))
     {
diff --git a/src/copy.h b/src/copy.h
index 440d3bb..d6044aa 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 61b31af..017e4da 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}
@@ -229,6 +230,7 @@ Mandatory arguments to long options are mandatory for short options too.\n\
                                  destination file is missing\n\
   -v, --verbose                explain what is being done\n\
   -x, --one-file-system        stay on this file system\n\
+  -Z, --context[=CONTEXT]      set security context of destination file to default type or to CONTEXT if specified\n\
 "), stdout);
       fputs (HELP_OPTION_DESCRIPTION, stdout);
       fputs (VERSION_OPTION_DESCRIPTION, stdout);
@@ -786,6 +788,7 @@ cp_option_init (struct cp_options *x)
   x->explicit_no_preserve_mode = false;
   x->preserve_security_context = false;
   x->require_preserve_context = false;
+  x->set_security_context = false;
   x->preserve_xattr = false;
   x->reduce_diagnostics = false;
   x->require_preserve_xattr = false;
@@ -877,8 +880,10 @@ 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;
+          if (! x->set_security_context) {
+            x->preserve_security_context = on_off;
+            x->require_preserve_context = on_off;
+          }
           break;
 
         case PRESERVE_XATTR:
@@ -892,7 +897,7 @@ decode_preserve_arg (char const *arg, struct cp_options *x, bool on_off)
           x->preserve_ownership = on_off;
           x->preserve_links = on_off;
           x->explicit_no_preserve_mode = !on_off;
-          if (selinux_enabled)
+          if (selinux_enabled && (! x->set_security_context))
             x->preserve_security_context = on_off;
           x->preserve_xattr = on_off;
           break;
@@ -935,7 +940,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)
     {
@@ -962,7 +967,7 @@ main (int argc, char **argv)
           x.preserve_mode = true;
           x.preserve_timestamps = true;
           x.require_preserve = true;
-          if (selinux_enabled)
+          if (selinux_enabled && (! x.set_security_context))
              x.preserve_security_context = true;
           x.preserve_xattr = true;
           x.reduce_diagnostics = true;
@@ -1092,6 +1097,26 @@ 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 ) {
+             fprintf( stderr, "Warning:  ignoring --context (-Z). "
+                              "It requires a SELinux enabled kernel.\n" );
+             break;
+         }
+         if (optarg) {
+           /* if there's a security_context given set new path
+              components to that context, too */
+           if ( setfscreatecon(optarg) < 0 ) {
+             (void) fprintf(stderr, _("cannot set default security context %s\n"), optarg);
+             exit( 1 );
+           }
+         }
+         x.set_security_context = true;
+         x.preserve_security_context = false;
+         break;
+
         case 'S':
           make_backups = true;
           backup_suffix_string = optarg;
diff --git a/src/install.c b/src/install.c
index 8ea5491..a1511bf 100644
--- a/src/install.c
+++ b/src/install.c
@@ -280,6 +280,7 @@ cp_option_init (struct cp_options *x)
   x->data_copy_required = true;
   x->require_preserve = false;
   x->require_preserve_context = false;
+  x->set_security_context = false;
   x->require_preserve_xattr = false;
   x->recursive = false;
   x->sparse_mode = SPARSE_AUTO;
@@ -641,7 +642,7 @@ Mandatory arguments to long options are mandatory for short options too.\n\
 "), stdout);
       fputs (_("\
       --preserve-context  preserve SELinux security context\n\
-  -Z, --context=CONTEXT  set SELinux security context of files and directories\
+  -Z, --context[=CONTEXT] set SELinux security context of files and directories\
 \n\
 "), stdout);
 
@@ -783,7 +784,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)
@@ -860,8 +861,11 @@ main (int argc, char **argv)
                              "this kernel is not SELinux-enabled"));
               break;
             }
+          if ( x.set_security_context || scontext ) {
+             (void) fprintf(stderr, "%s: cannot force target context and preserve it\n", argv[0]);
+             exit( 1 );
+          }
           x.preserve_security_context = true;
-          use_default_selinux_context = false;
           break;
         case 'Z':
           if ( ! selinux_enabled)
@@ -870,8 +874,10 @@ main (int argc, char **argv)
                              "this kernel is not SELinux-enabled"));
               break;
             }
-          scontext = optarg;
-          use_default_selinux_context = false;
+          if (optarg)
+                  scontext = optarg;
+          else
+                  x.set_security_context = true;
           break;
         case_GETOPT_HELP_CHAR;
         case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
diff --git a/src/local.mk b/src/local.mk
index f40f681..113544a 100644
--- a/src/local.mk
+++ b/src/local.mk
@@ -306,6 +306,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 \
@@ -317,12 +321,12 @@ 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
@@ -335,12 +339,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 32f79d4..80e9fa5 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"
 
 /* The official name of this program (e.g., no 'g' prefix).  */
 #define PROGRAM_NAME "mkdir"
@@ -65,8 +66,8 @@ Mandatory arguments to long options are mandatory for short options too.\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.  */
+  int set_security_context;
+
   /* If not null, format to use when reporting newly made directories.  */
   char const *created_directory_format;
 };
@@ -113,6 +117,9 @@ static int
 make_ancestor (char const *dir, char const *component, void *options)
 {
   struct mkdir_options const *o = options;
+
+  if (o->set_security_context)
+    defaultcon(dir, S_IFDIR);
   int r = mkdir (component, o->ancestor_mode);
   if (r == 0)
     {
@@ -146,6 +153,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]);
@@ -155,7 +163,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)
         {
@@ -169,7 +177,10 @@ main (int argc, char **argv)
           options.created_directory_format = _("created directory %s");
           break;
         case 'Z':
-          scontext = optarg;
+          if (optarg)
+            scontext = optarg;
+          else
+            options.set_security_context = true;
           break;
         case_GETOPT_HELP_CHAR;
         case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
diff --git a/src/mkfifo.c b/src/mkfifo.c
index e524c44..bd51a53 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"
 
 /* The official name of this program (e.g., no 'g' prefix).  */
 #define PROGRAM_NAME "mkfifo"
@@ -60,7 +61,7 @@ Mandatory arguments to long options are mandatory for short options too.\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 default type or CTX if specified\n\
 "), stdout);
       fputs (HELP_OPTION_DESCRIPTION, stdout);
       fputs (VERSION_OPTION_DESCRIPTION, stdout);
@@ -74,6 +75,7 @@ main (int argc, char **argv)
 {
   mode_t newmode;
   char const *specified_mode = NULL;
+  int set_security_context = false;
   int exit_status = EXIT_SUCCESS;
   int optc;
   security_context_t scontext = NULL;
@@ -86,7 +88,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 +96,10 @@ main (int argc, char **argv)
           specified_mode = optarg;
           break;
         case 'Z':
-          scontext = optarg;
+          if (optarg)
+            scontext = optarg;
+          else
+            set_security_context = true;
           break;
         case_GETOPT_HELP_CHAR;
         case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
@@ -128,6 +133,8 @@ main (int argc, char **argv)
     }
 
   for (; optind < argc; ++optind)
+    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]));
diff --git a/src/mknod.c b/src/mknod.c
index dc158b4..b1b35eb 100644
--- a/src/mknod.c
+++ b/src/mknod.c
@@ -27,6 +27,7 @@
 #include "modechange.h"
 #include "quote.h"
 #include "xstrtol.h"
+#include "selinux.h"
 
 /* The official name of this program (e.g., no 'g' prefix).  */
 #define PROGRAM_NAME "mknod"
@@ -62,7 +63,7 @@ Mandatory arguments to long options are mandatory for short options too.\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 default type or to CTX if specified\n\
 "), stdout);
       fputs (HELP_OPTION_DESCRIPTION, stdout);
       fputs (VERSION_OPTION_DESCRIPTION, stdout);
@@ -94,6 +95,7 @@ main (int argc, char **argv)
   int expected_operands;
   mode_t node_type;
   security_context_t scontext = NULL;
+  int set_security_context = false;
 
   initialize_main (&argc, &argv);
   set_program_name (argv[0]);
@@ -103,7 +105,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 +113,10 @@ main (int argc, char **argv)
           specified_mode = optarg;
           break;
         case 'Z':
-          scontext = optarg;
+          if (optarg)
+            scontext = optarg;
+          else
+            set_security_context = true;
           break;
         case_GETOPT_HELP_CHAR;
         case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
@@ -212,6 +217,9 @@ 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]));
       }
diff --git a/src/mv.c b/src/mv.c
index 5b08fdd..2711d9f 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 */
@@ -317,6 +319,7 @@ 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 security context of destination file to default type\n \
 "), stdout);
       fputs (HELP_OPTION_DESCRIPTION, stdout);
       fputs (VERSION_OPTION_DESCRIPTION, stdout);
@@ -351,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]);
@@ -369,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 +422,16 @@ 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 ) {
+             fprintf( stderr, "Warning:  ignoring --context (-Z). "
+                              "It requires a SELinux enabled kernel.\n" );
+             break;
+          }
+          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 875441f..7162f65 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\
 \n\
   CONTEXT            Complete security context\n\
diff --git a/src/selinux.c b/src/selinux.c
new file mode 100644
index 0000000..6045dd5
--- /dev/null
+++ b/src/selinux.c
@@ -0,0 +1,269 @@
+/* selinux - core functions for maintaining SELinux labelking
+   Copyright (C) 2012 Red Hat, 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 <http://www.gnu.org/licenses/>.  */
+
+/* Written by Daniel Walsh <[email protected]> */
+
+#include <config.h>
+#include <selinux/selinux.h>
+#include <selinux/flask.h>
+#include <selinux/context.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <libgen.h>
+#include <string.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <unistd.h>
+#include "selinux.h"
+
+#include "error.h"
+#include "system.h"
+#include "fts.h"
+
+/*
+  This function has being added to libselinux-2.1.12-5, but is here
+  for support with older versions of SELinux
+
+  Translates a mode into an Internal SELinux security_class definition.
+  Returns 0 on failure, with errno set to EINVAL.
+*/
+static security_class_t mode_to_security_class(mode_t m) {
+
+  if (S_ISREG(m))
+    return string_to_security_class("file");
+  if (S_ISDIR(m))
+    return string_to_security_class("dir");
+  if (S_ISCHR(m))
+    return string_to_security_class("chr_file");
+  if (S_ISBLK(m))
+    return string_to_security_class("blk_file");
+  if (S_ISFIFO(m))
+    return string_to_security_class("fifo_file");
+  if (S_ISLNK(m))
+    return string_to_security_class("lnk_file");
+  if (S_ISSOCK(m))
+    return string_to_security_class("sock_file");
+
+  errno=EINVAL;
+  return 0;
+}
+
+/*
+  This function takes a path and a mode and then asks SELinux what the label
+  of the path object would be if the current process label created it.
+  it then returns the label.
+
+  Returns -1 on failure. errno will be set approptiately.
+*/
+
+static int computecon(char const *path, mode_t mode, security_context_t *con) {
+  security_context_t scon = NULL;
+  security_context_t tcon = NULL;
+  security_class_t tclass;
+  int rc = -1;
+
+  char *dir = strdup(path);
+  if (!dir)
+    goto quit;
+  if (getcon(&scon) < 0)
+    goto quit;
+  if (getfilecon(dirname((char *) dir), &tcon) < 0)
+    goto quit;
+  tclass = mode_to_security_class(mode);
+  if (!tclass)
+    goto quit;
+  rc = security_compute_create(scon, tcon, tclass, con);
+
+quit:
+  free(dir);
+  freecon(scon);
+  freecon(tcon);
+  return rc;
+}
+
+/*
+  This function takes a path and a mode, it asks calls computecon to get the
+  label of the path object if the current process created it, then it calls
+  matchpathcon to get the default type for the object.  It substitutes the
+  default type into label.  It tells the SELinux Kernel to label all new file
+  system objects created by the current process with this label.
+
+  Returns -1 on failure. errno will be set approptiately.
+*/
+int defaultcon (char const *path, mode_t mode) {
+  int rc = -1;
+  security_context_t scon = NULL, tcon = NULL;
+  context_t scontext = NULL, tcontext = NULL;
+
+  rc = matchpathcon(path, mode,  &scon);
+  if (rc < 0)
+    goto quit;
+  rc = computecon(path, mode,  &tcon);
+  if (rc < 0)
+    goto quit;
+  scontext = context_new(scon);
+  rc = -1;
+  if (!scontext)
+    goto quit;
+  tcontext = context_new(tcon);
+  if (!tcontext)
+    goto quit;
+
+  context_type_set(tcontext, context_type_get(scontext));
+  rc = setfscreatecon (context_str(tcontext));
+
+//  printf("defaultcon %s %s\n", path, context_str(tcontext));
+quit:
+  if (scontext)
+    context_free(scontext);
+  if (scontext)
+    context_free(tcontext);
+  freecon(scon);
+  freecon(tcon);
+  return rc;
+}
+
+/*
+  This function takes a path of an existing file system object, and a boolean
+  that indicates whether the function should preserve the objects label or
+  generate a new label using matchpathcon.  If the function
+  is called with preserve, it will ask the SELinux Kernel what the default label
+  for all objects created should be and then sets the label on the object.
+  Otherwise it calls matchpathcon on the object to ask the system what the
+  default label should be, extracts the type field and then modifies the file
+  system object.
+
+  Returns -1 on failure. errno will be set approptiately.
+*/
+static int restorecon_private (char const *path, bool preserve) {
+  int rc = -1;
+  struct stat sb;
+  security_context_t scon = NULL, tcon = NULL;
+  context_t scontext = NULL, tcontext = NULL;
+  int fd;
+
+  if (preserve) {
+    if (getfscreatecon (&tcon) < 0)
+      return rc;
+    rc = lsetfilecon (path, tcon);
+    freecon(tcon);
+    return rc;
+  }
+
+  fd = open (path, O_RDONLY | O_NOFOLLOW);
+  if (!fd && (errno != ELOOP))
+    goto quit;
+
+  if (fd) {
+    rc = fstat (fd, &sb);
+    if (rc < 0)
+      goto quit;
+  } else {
+    rc = lstat (path, &sb);
+    if (rc < 0)
+      goto quit;
+  }
+
+  rc = matchpathcon(path, sb.st_mode,  &scon);
+  if (rc < 0)
+    goto quit;
+  scontext = context_new(scon);
+  rc = -1;
+  if (!scontext)
+    goto quit;
+
+  if (fd) {
+    rc = fgetfilecon (fd, &tcon);
+    if (!rc)
+      goto quit;
+  } else  {
+    rc = lgetfilecon (path, &tcon);
+    if (!rc)
+      goto quit;
+  }
+  tcontext = context_new(tcon);
+  if (!tcontext)
+    goto quit;
+
+  context_type_set(tcontext, context_type_get(scontext));
+
+  if (fd)
+        rc = fsetfilecon (fd, context_str(tcontext));
+  else
+        rc = lsetfilecon (path, context_str(tcontext));
+
+//  printf("restorcon %s %s\n", path, context_str(tcontext));
+quit:
+  close(fd);
+  if (scontext)
+    context_free(scontext);
+  if (scontext)
+    context_free(tcontext);
+  freecon(scon);
+  freecon(tcon);
+  return rc;
+}
+
+/*
+  This function takes three parameters:
+  Path of an existing file system object.
+  A boolean indicating whether it should call restorecon_private recursively
+  or not.
+  A boolean that indicates whether the function should preserve the objects
+  label or generate a new label using matchpathcon.
+
+  If Recurse is selected and the file system object is a directory, restorecon
+  calls restorecon_private on every file system object in the directory.
+
+  Returns false on failure. errno will be set approptiately.
+*/
+bool restorecon (char const *path, bool recurse, bool preserve) {
+  const char *mypath[2] = { path, NULL };
+  FTS *fts;
+  bool ok = true;
+
+  if (!recurse)
+    return restorecon_private(path, preserve);
+
+  fts = fts_open ((char *const *)mypath, FTS_PHYSICAL, NULL);
+  while (1)
+    {
+      FTSENT *ent;
+
+      ent = fts_read (fts);
+      if (ent == NULL)
+        {
+          if (errno != 0)
+            {
+              /* FIXME: try to give a better message  */
+              error (0, errno, _("fts_read failed"));
+              ok = false;
+            }
+          break;
+        }
+
+      ok &= restorecon_private(fts->fts_path, preserve);
+    }
+
+  if (fts_close (fts) != 0)
+    {
+      error (0, errno, _("fts_close failed"));
+      ok = false;
+    }
+
+  return ok;
+}
diff --git a/src/selinux.h b/src/selinux.h
new file mode 100644
index 0000000..c032c05
--- /dev/null
+++ b/src/selinux.h
@@ -0,0 +1,26 @@
+/* selinux - core functions for maintaining SELinux labelking
+   Copyright (C) 2012 Red Hat, 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 <http://www.gnu.org/licenses/>.  */
+
+/* Written by Daniel Walsh <[email protected]> */
+
+#ifndef COREUTILS_SELINUX_H
+#define COREUTILS_SELINUX_H
+
+#include <stdbool.h>
+#include <sys/stat.h>
+extern bool restorecon (char const *path, bool recurse, bool preserve);
+extern int defaultcon (char const *path, mode_t mode);
+#endif
-- 
1.7.12.1

Reply via email to