--- chcon.c~ 2003-07-13 22:42:47.000000000 -0400 +++ chcon.c 2003-07-13 22:33:06.000000000 -0400 @@ -1,11 +1,15 @@ /* chcontext -- change security context of a pathname */ +/* Added the ability to specify individual components of + a security context. -- Colin Walters */ + #include #include #include #include #include #include +#include #include "system.h" #include "error.h" @@ -31,8 +35,7 @@ V_off }; -static int change_dir_context PARAMS ((const char *dir, security_context_t context, - const struct stat *statp)); +static int change_dir_context PARAMS ((const char *dir, const struct stat *statp)); /* The name the program was run with. */ char *program_name; @@ -51,7 +54,13 @@ static enum Verbosity verbosity = V_off; /* The name of the context file is being given. */ -static const char *contextname; +static const char *specified_context; + +/* Specific components of the context */ +static const char *specified_user; +static const char *specified_role; +static const char *specified_range; +static const char *specified_type; /* The argument to the --reference option. Use the context of this file. This file must exist. */ @@ -72,6 +81,10 @@ {"quiet", no_argument, 0, 'f'}, {"reference", required_argument, 0, CHAR_MAX + 1}, {"context", required_argument, 0, CHAR_MAX + 2}, + {"user", required_argument, 0, 'u'}, + {"role", required_argument, 0, 'r'}, + {"type", required_argument, 0, 't'}, + {"range", required_argument, 0, 'l'}, {"verbose", no_argument, 0, 'v'}, {"help", no_argument, &show_help, 1}, {"version", no_argument, &show_version, 1}, @@ -82,7 +95,7 @@ CHANGED describes what (if anything) has happened. */ static void -describe_change (const char *file, enum Change_status changed) +describe_change (const char *file, security_context_t newcontext, enum Change_status changed) { const char *fmt; switch (changed) @@ -99,71 +112,128 @@ default: abort (); } - printf (fmt, file, contextname); + printf (fmt, file, newcontext); +} + +static int +compute_context_from_mask (security_context_t context, context_t *ret) +{ + context_t newcontext = context_new (context); + if (!newcontext) + return 1; +#define SETCOMPONENT(comp) \ + do { \ + if (specified_ ## comp) \ + if (context_ ## comp ## _set (newcontext, specified_ ## comp)) \ + goto lose; \ + } while (0) + + SETCOMPONENT(user); + SETCOMPONENT(range); + SETCOMPONENT(role); + SETCOMPONENT(type); +#undef SETCOMPONENT + + *ret = newcontext; + return 0; + lose: + context_free (newcontext); + return 1; } -/* Change the context of FILE to CONTEXT. +/* Change the context of FILE, using specified components. If it is a directory and -R is given, recurse. Return 0 if successful, 1 if errors occurred. */ static int -change_file_context (const char *file, security_context_t context) +change_file_context (const char *file) { struct stat file_stats; security_context_t file_context=NULL; + context_t context; + security_context_t context_string; int errors = 0; if ((lgetfilecon(file, &file_context)<0) && (errno != ENODATA)) - { if (force_silent == 0) error (0, errno, "%s", file); return 1; } - if ((file_context==NULL) || strcmp(context,file_context)!=0) + /* This is kind of a gross hack. But if the file doesn't have a + context, and we're not setting all of the context components, + there isn't really a sensible default. Thus, we just get the + process context, and hope for the best. */ + if (file_context == NULL && specified_context == NULL) + if (getcon (&file_context) < 0) + { + if (force_silent == 0) + error (0, errno, _("couldn't get default context")); + return 1; + } + + if (specified_context == NULL) + { + if (compute_context_from_mask (file_context, &context)) + { + error (0, 0, _("couldn't compute security context from %s"), file_context); + return 1; + } + } + else + { + context = context_new (specified_context); + if (!context) + error (1, 0, _("virtual memory exhausted")); + } + + context_string = context_str (context); + + if (strcmp(context_string,file_context)!=0) { int fail; if (change_symlinks) - fail = lsetfilecon (file, context); + fail = lsetfilecon (file, context_string); else - fail = setfilecon (file, context); + fail = setfilecon (file, context_string); if (verbosity == V_high || (verbosity == V_changes_only && !fail)) - describe_change (file, (fail ? CH_FAILED : CH_SUCCEEDED)); + describe_change (file, context_string, (fail ? CH_FAILED : CH_SUCCEEDED)); if (fail) { errors = 1; if (force_silent == 0) { - error (0, errno, "%s", file); + error (0, errno, _("failed to change context of %s to %s"), file, context_string); } } } else if (verbosity == V_high) { - describe_change (file, CH_NO_CHANGE_REQUESTED); + describe_change (file, context_string, CH_NO_CHANGE_REQUESTED); } + context_free(context); freecon(file_context); if (recurse) { if (lstat(file, &file_stats)==0) if (S_ISDIR (file_stats.st_mode)) - errors |= change_dir_context (file, context, &file_stats); + errors |= change_dir_context (file, &file_stats); } return errors; } /* Recursively change context of the files in directory DIR - to CONTEXT CONTEXT. + using specified context components. STATP points to the results of lstat on DIR. Return 0 if successful, 1 if errors occurred. */ static int -change_dir_context (const char *dir, security_context_t context, const struct stat *statp) +change_dir_context (const char *dir, const struct stat *statp) { char *name_space, *namep; char *path; /* Full path of each entry to process. */ @@ -202,7 +272,7 @@ path = xrealloc (path, pathlength); } strcpy (path + dirlength, namep); - errors |= change_file_context (path, context); + errors |= change_file_context (path); } free (path); free (name_space); @@ -219,6 +289,7 @@ { 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); @@ -230,6 +301,10 @@ (available only on systems with lchown system call)\n\ -f, --silent, --quiet suppress most error messages\n\ --reference=RFILE use RFILE's group instead of using a CONTEXT value\n\ + -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\ -R, --recursive change files and directories recursively\n\ -v, --verbose output a diagnostic for every file processed\n\ --help display this help and exit\n\ @@ -243,10 +318,10 @@ int main (int argc, char **argv) { - security_context_t context = NULL; security_context_t ref_context = NULL; int errors = 0; int optc; + int component_specified = 0; program_name = argv[0]; setlocale (LC_ALL, ""); @@ -255,12 +330,28 @@ recurse = force_silent = 0; - while ((optc = getopt_long (argc, argv, "Rcfhv", long_options, NULL)) != -1) + while ((optc = getopt_long (argc, argv, "Rcfhvu:r:t:l:", long_options, NULL)) != -1) { switch (optc) { case 0: - break; + break; + case 'u': + specified_user = optarg; + component_specified = 1; + break; + case 'r': + specified_role = optarg; + component_specified = 1; + break; + case 't': + specified_type = optarg; + component_specified = 1; + break; + case 'l': + specified_range = optarg; + component_specified = 1; + break; case CHAR_MAX + 1: reference_file = optarg; break; @@ -294,24 +385,33 @@ if (show_help) usage (0); - if (argc - optind + ( (reference_file || ( context > 0 ) ) ? 1 : 0) <= 1) - { - error (0, 0, _("too few arguments")); - usage (1); - } + + if (reference_file && component_specified) + { + error (0, 0, _("conflicting security context specifiers given")); + usage (1); + } + + if (!(((reference_file || component_specified) + && (argc - optind > 0)) + || (argc - optind > 1))) + { + error (0, 0, _("too few arguments")); + usage (1); + } if (reference_file) { if (getfilecon (reference_file, &ref_context)<0) error (1, errno, "%s", reference_file); - - context = ref_context; + + specified_context = ref_context; } - else { - context = argv[optind++]; + else if (!component_specified) { + specified_context = argv[optind++]; } for (; optind < argc; ++optind) - errors |= change_file_context (argv[optind], context); + errors |= change_file_context (argv[optind]); if (verbosity != V_off) close_stdout ();