src/chcon.c - coreutils-8.23
Global variables defined
Functions defined
Macros defined
Source code
#include <config.h>
#include <stdio.h>
#include <sys/types.h>
#include <getopt.h>
#include "system.h"
#include "dev-ino.h"
#include "error.h"
#include "ignore-value.h"
#include "quote.h"
#include "quotearg.h"
#include "root-dev-ino.h"
#include "selinux-at.h"
#include "xfts.h"
#define PROGRAM_NAME "chcon"
#define AUTHORS \
proper_name ("Russell Coker"), \
proper_name ("Jim Meyering")
static bool affect_symlink_referent;
static bool recurse;
static bool verbose;
static struct dev_ino *root_dev_ino;
static char const *specified_context;
static char const *specified_user;
static char const *specified_role;
static char const *specified_range;
static char const *specified_type;
enum
{
DEREFERENCE_OPTION = CHAR_MAX + 1,
NO_PRESERVE_ROOT,
PRESERVE_ROOT,
REFERENCE_FILE_OPTION
};
static struct option const long_options[] =
{
{"recursive", no_argument, NULL, 'R'},
{"dereference", no_argument, NULL, DEREFERENCE_OPTION},
{"no-dereference", no_argument, NULL, 'h'},
{"no-preserve-root", no_argument, NULL, NO_PRESERVE_ROOT},
{"preserve-root", no_argument, NULL, PRESERVE_ROOT},
{"reference", required_argument, NULL, REFERENCE_FILE_OPTION},
{"user", required_argument, NULL, 'u'},
{"role", required_argument, NULL, 'r'},
{"type", required_argument, NULL, 't'},
{"range", required_argument, NULL, 'l'},
{"verbose", no_argument, NULL, 'v'},
{GETOPT_HELP_OPTION_DECL},
{GETOPT_VERSION_OPTION_DECL},
{NULL, 0, NULL, 0}
};
static int
compute_context_from_mask (char const *context, context_t *ret)
{
bool ok = true;
context_t new_context = context_new (context);
if (!new_context)
{
error (0, errno, _("failed to create security context: %s"),
quotearg_colon (context));
return 1;
}
#define SET_COMPONENT(C, comp) \
do \
{ \
if (specified_ ## comp \
&& context_ ## comp ## _set ((C), specified_ ## comp)) \
{ \
error (0, errno, \
_("failed to set %s security context component to %s"), \
#comp, quote (specified_ ## comp)); \
ok = false; \
} \
} \
while (0)
SET_COMPONENT (new_context, user);
SET_COMPONENT (new_context, range);
SET_COMPONENT (new_context, role);
SET_COMPONENT (new_context, type);
if (!ok)
{
int saved_errno = errno;
context_free (new_context);
errno = saved_errno;
return 1;
}
*ret = new_context;
return 0;
}
static int
change_file_context (int fd, char const *file)
{
char *file_context = NULL;
context_t context IF_LINT (= 0);
char const * context_string;
int errors = 0;
if (specified_context == NULL)
{
int status = (affect_symlink_referent
? getfileconat (fd, file, &file_context)
: lgetfileconat (fd, file, &file_context));
if (status < 0 && errno != ENODATA)
{
error (0, errno, _("failed to get security context of %s"),
quote (file));
return 1;
}
if (file_context == NULL)
{
error (0, 0, _("can't apply partial context to unlabeled file %s"),
quote (file));
return 1;
}
if (compute_context_from_mask (file_context, &context))
return 1;
context_string = context_str (context);
}
else
{
context_string = specified_context;
}
if (file_context == NULL || ! STREQ (context_string, file_context))
{
int fail = (affect_symlink_referent
? setfileconat (fd, file, se_const (context_string))
: lsetfileconat (fd, file, se_const (context_string)));
if (fail)
{
errors = 1;
error (0, errno, _("failed to change context of %s to %s"),
quote_n (0, file), quote_n (1, context_string));
}
}
if (specified_context == NULL)
{
context_free (context);
freecon (file_context);
}
return errors;
}
static bool
process_file (FTS *fts, FTSENT *ent)
{
char const *file_full_name = ent->fts_path;
char const *file = ent->fts_accpath;
const struct stat *file_stats = ent->fts_statp;
bool ok = true;
switch (ent->fts_info)
{
case FTS_D:
if (recurse)
{
if (ROOT_DEV_INO_CHECK (root_dev_ino, ent->fts_statp))
{
ROOT_DEV_INO_WARN (file_full_name);
fts_set (fts, ent, FTS_SKIP);
ignore_value (fts_read (fts));
return false;
}
return true;
}
break;
case FTS_DP:
if (! recurse)
return true;
break;
case FTS_NS:
if (ent->fts_level == 0 && ent->fts_number == 0)
{
ent->fts_number = 1;
fts_set (fts, ent, FTS_AGAIN);
return true;
}
error (0, ent->fts_errno, _("cannot access %s"), quote (file_full_name));
ok = false;
break;
case FTS_ERR:
error (0, ent->fts_errno, "%s", quote (file_full_name));
ok = false;
break;
case FTS_DNR:
error (0, ent->fts_errno, _("cannot read directory %s"),
quote (file_full_name));
ok = false;
break;
case FTS_DC: if (cycle_warning_required (fts, ent))
{
emit_cycle_warning (file_full_name);
return false;
}
break;
default:
break;
}
if (ent->fts_info == FTS_DP
&& ok && ROOT_DEV_INO_CHECK (root_dev_ino, file_stats))
{
ROOT_DEV_INO_WARN (file_full_name);
ok = false;
}
if (ok)
{
if (verbose)
printf (_("changing security context of %s\n"),
quote (file_full_name));
if (change_file_context (fts->fts_cwd_fd, file) != 0)
ok = false;
}
if ( ! recurse)
fts_set (fts, ent, FTS_SKIP);
return ok;
}
static bool
process_files (char **files, int bit_flags)
{
bool ok = true;
FTS *fts = xfts_open (files, bit_flags, NULL);
while (1)
{
FTSENT *ent;
ent = fts_read (fts);
if (ent == NULL)
{
if (errno != 0)
{
FIXME error (0, errno, _("fts_read failed"));
ok = false;
}
break;
}
ok &= process_file (fts, ent);
}
if (fts_close (fts) != 0)
{
error (0, errno, _("fts_close failed"));
ok = false;
}
return ok;
}
void
usage (int status)
{
if (status != EXIT_SUCCESS)
emit_try_help ();
else
{
printf (_("\
Usage: %s [OPTION]... CONTEXT FILE...\n\
or: %s [OPTION]... [-u USER] [-r ROLE] [-l RANGE] [-t TYPE] FILE...\n\
or: %s [OPTION]... --reference=RFILE FILE...\n\
"),
program_name, program_name, program_name);
fputs (_("\
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);
emit_mandatory_arg_note ();
fputs (_("\
--dereference affect the referent of each symbolic link (this is\n\
the default), rather than the symbolic link itself\n\
-h, --no-dereference affect symbolic links instead of any referenced file\n\
"), stdout);
fputs (_("\
-u, --user=USER set user USER in the target security context\n\
-r, --role=ROLE set role ROLE in the target security context\n\
-t, --type=TYPE set type TYPE in the target security context\n\
-l, --range=RANGE set range RANGE in the target security context\n\
"), stdout);
fputs (_("\
--no-preserve-root do not treat '/' specially (the default)\n\
--preserve-root fail to operate recursively on '/'\n\
"), stdout);
fputs (_("\
--reference=RFILE use RFILE's security context rather than specifying\n\
a CONTEXT value\n\
"), stdout);
fputs (_("\
-R, --recursive operate on files and directories recursively\n\
"), stdout);
fputs (_("\
-v, --verbose output a diagnostic for every file processed\n\
"), stdout);
fputs (_("\
\n\
The following options modify how a hierarchy is traversed when the -R\n\
option is also specified. If more than one is specified, only the final\n\
one takes effect.\n\
\n\
-H if a command line argument is a symbolic link\n\
to a directory, traverse it\n\
-L traverse every symbolic link to a directory\n\
encountered\n\
-P do not traverse any symbolic links (default)\n\
\n\
"), stdout);
fputs (HELP_OPTION_DESCRIPTION, stdout);
fputs (VERSION_OPTION_DESCRIPTION, stdout);
emit_ancillary_info ();
}
exit (status);
}
int
main (int argc, char **argv)
{
int bit_flags = FTS_PHYSICAL;
int dereference = -1;
bool ok;
bool preserve_root = false;
bool component_specified = false;
char *reference_file = NULL;
int optc;
initialize_main (&argc, &argv);
set_program_name (argv[0]);
setlocale (LC_ALL, "");
bindtextdomain (PACKAGE, LOCALEDIR);
textdomain (PACKAGE);
atexit (close_stdout);
while ((optc = getopt_long (argc, argv, "HLPRhvu:r:t:l:", long_options, NULL))
!= -1)
{
switch (optc)
{
case 'H': bit_flags = FTS_COMFOLLOW | FTS_PHYSICAL;
break;
case 'L': bit_flags = FTS_LOGICAL;
break;
case 'P': bit_flags = FTS_PHYSICAL;
break;
case 'h': dereference = 0;
break;
case DEREFERENCE_OPTION: dereference = 1;
break;
case NO_PRESERVE_ROOT:
preserve_root = false;
break;
case PRESERVE_ROOT:
preserve_root = true;
break;
case REFERENCE_FILE_OPTION:
reference_file = optarg;
break;
case 'R':
recurse = true;
break;
case 'f':
break;
case 'v':
verbose = true;
break;
case 'u':
specified_user = optarg;
component_specified = true;
break;
case 'r':
specified_role = optarg;
component_specified = true;
break;
case 't':
specified_type = optarg;
component_specified = true;
break;
case 'l':
specified_range = optarg;
component_specified = true;
break;
case_GETOPT_HELP_CHAR;
case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
default:
usage (EXIT_FAILURE);
}
}
if (recurse)
{
if (bit_flags == FTS_PHYSICAL)
{
if (dereference == 1)
error (EXIT_FAILURE, 0,
_("-R --dereference requires either -H or -L"));
affect_symlink_referent = false;
}
else
{
if (dereference == 0)
error (EXIT_FAILURE, 0, _("-R -h requires -P"));
affect_symlink_referent = true;
}
}
else
{
bit_flags = FTS_PHYSICAL;
affect_symlink_referent = (dereference != 0);
}
if (argc - optind < (reference_file || component_specified ? 1 : 2))
{
if (argc <= optind)
error (0, 0, _("missing operand"));
else
error (0, 0, _("missing operand after %s"), quote (argv[argc - 1]));
usage (EXIT_FAILURE);
}
if (reference_file)
{
char *ref_context = NULL;
if (getfilecon (reference_file, &ref_context) < 0)
error (EXIT_FAILURE, errno, _("failed to get security context of %s"),
quote (reference_file));
specified_context = ref_context;
}
else if (component_specified)
{
FIXME specified_context = NULL;
}
else
{
specified_context = argv[optind++];
if (security_check_context (se_const (specified_context)) < 0)
error (EXIT_FAILURE, errno, _("invalid context: %s"),
quotearg_colon (specified_context));
}
if (reference_file && component_specified)
{
error (0, 0, _("conflicting security context specifiers given"));
usage (EXIT_FAILURE);
}
if (recurse && preserve_root)
{
static struct dev_ino dev_ino_buf;
root_dev_ino = get_root_dev_ino (&dev_ino_buf);
if (root_dev_ino == NULL)
error (EXIT_FAILURE, errno, _("failed to get attributes of %s"),
quote ("/"));
}
else
{
root_dev_ino = NULL;
}
ok = process_files (argv + optind, bit_flags | FTS_NOSTAT);
exit (ok ? EXIT_SUCCESS : EXIT_FAILURE);
}