| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
|
|
| #include <config.h> |
| #include <stdio.h> |
| #include <sys/types.h> |
| #include <getopt.h> |
| #include <selinux/selinux.h> |
|
|
| #include "system.h" |
| #include "dev-ino.h" |
| #include "ignore-value.h" |
| #include "quote.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, nullptr, 'R'}, |
| {"dereference", no_argument, nullptr, DEREFERENCE_OPTION}, |
| {"no-dereference", no_argument, nullptr, 'h'}, |
| {"no-preserve-root", no_argument, nullptr, NO_PRESERVE_ROOT}, |
| {"preserve-root", no_argument, nullptr, PRESERVE_ROOT}, |
| {"reference", required_argument, nullptr, REFERENCE_FILE_OPTION}, |
| {"user", required_argument, nullptr, 'u'}, |
| {"role", required_argument, nullptr, 'r'}, |
| {"type", required_argument, nullptr, 't'}, |
| {"range", required_argument, nullptr, 'l'}, |
| {"verbose", no_argument, nullptr, 'v'}, |
| {GETOPT_HELP_OPTION_DECL}, |
| {GETOPT_VERSION_OPTION_DECL}, |
| {nullptr, 0, nullptr, 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"), |
| quote (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 = nullptr; |
| context_t context IF_LINT (= 0); |
| char const * context_string; |
| int errors = 0; |
|
|
| if (specified_context == nullptr) |
| { |
| 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"), |
| quoteaf (file)); |
| return 1; |
| } |
|
|
| |
| |
| |
| if (file_context == nullptr) |
| { |
| error (0, 0, _("can't apply partial context to unlabeled file %s"), |
| quoteaf (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 == nullptr || ! streq (context_string, file_context)) |
| { |
| int fail = (affect_symlink_referent |
| ? setfileconat (fd, file, context_string) |
| : lsetfileconat (fd, file, context_string)); |
|
|
| if (fail) |
| { |
| errors = 1; |
| error (0, errno, _("failed to change context of %s to %s"), |
| quoteaf_n (0, file), quote_n (1, context_string)); |
| } |
| } |
|
|
| if (specified_context == nullptr) |
| { |
| 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"), |
| quoteaf (file_full_name)); |
| ok = false; |
| break; |
|
|
| case FTS_ERR: |
| error (0, ent->fts_errno, "%s", quotef (file_full_name)); |
| ok = false; |
| break; |
|
|
| case FTS_DNR: |
| error (0, ent->fts_errno, _("cannot read directory %s"), |
| quoteaf (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"), |
| quoteaf (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, nullptr); |
|
|
| while (true) |
| { |
| FTSENT *ent; |
|
|
| ent = fts_read (fts); |
| if (ent == nullptr) |
| { |
| if (errno != 0) |
| { |
| |
| 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 (PROGRAM_NAME); |
| } |
| 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 = nullptr; |
| 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, nullptr)) |
| != -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 = nullptr; |
|
|
| if (getfilecon (reference_file, &ref_context) < 0) |
| error (EXIT_FAILURE, errno, _("failed to get security context of %s"), |
| quoteaf (reference_file)); |
|
|
| specified_context = ref_context; |
| } |
| else if (component_specified) |
| { |
| |
| specified_context = nullptr; |
| } |
| else |
| { |
| specified_context = argv[optind++]; |
| if (0 < is_selinux_enabled () |
| && security_check_context (specified_context) < 0) |
| error (EXIT_FAILURE, errno, _("invalid context: %s"), |
| quote (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 == nullptr) |
| error (EXIT_FAILURE, errno, _("failed to get attributes of %s"), |
| quoteaf ("/")); |
| } |
| else |
| { |
| root_dev_ino = nullptr; |
| } |
|
|
| ok = process_files (argv + optind, bit_flags | FTS_NOSTAT); |
|
|
| return ok ? EXIT_SUCCESS : EXIT_FAILURE; |
| } |
|
|