| /* | 
 |  * chcon -- change security context, based on coreutils-5.97-13 | 
 |  * | 
 |  * Port to busybox: KaiGai Kohei <kaigai@kaigai.gr.jp> | 
 |  * | 
 |  * Copyright (C) 2006 - 2007 KaiGai Kohei <kaigai@kaigai.gr.jp> | 
 |  * | 
 |  * Licensed under GPLv2, see file LICENSE in this source tree. | 
 |  */ | 
 |  | 
 | //usage:#define chcon_trivial_usage | 
 | //usage:       "[OPTIONS] CONTEXT FILE..." | 
 | //usage:       "\n	chcon [OPTIONS] [-u USER] [-r ROLE] [-l RANGE] [-t TYPE] FILE..." | 
 | //usage:	IF_FEATURE_CHCON_LONG_OPTIONS( | 
 | //usage:       "\n	chcon [OPTIONS] --reference=RFILE FILE..." | 
 | //usage:	) | 
 | //usage:#define chcon_full_usage "\n\n" | 
 | //usage:       "Change the security context of each FILE to CONTEXT\n" | 
 | //usage:	IF_FEATURE_CHCON_LONG_OPTIONS( | 
 | //usage:     "\n	-v,--verbose		Verbose" | 
 | //usage:     "\n	-c,--changes		Report changes made" | 
 | //usage:     "\n	-h,--no-dereference	Affect symlinks instead of their targets" | 
 | //usage:     "\n	-f,--silent,--quiet	Suppress most error messages" | 
 | //usage:     "\n	--reference=RFILE	Use RFILE's group instead of using a CONTEXT value" | 
 | //usage:     "\n	-u,--user=USER		Set user/role/type/range in the target" | 
 | //usage:     "\n	-r,--role=ROLE		security context" | 
 | //usage:     "\n	-t,--type=TYPE" | 
 | //usage:     "\n	-l,--range=RANGE" | 
 | //usage:     "\n	-R,--recursive		Recurse" | 
 | //usage:	) | 
 | //usage:	IF_NOT_FEATURE_CHCON_LONG_OPTIONS( | 
 | //usage:     "\n	-v	Verbose" | 
 | //usage:     "\n	-c	Report changes made" | 
 | //usage:     "\n	-h	Affect symlinks instead of their targets" | 
 | //usage:     "\n	-f	Suppress most error messages" | 
 | //usage:     "\n	-u USER	Set user/role/type/range in the target security context" | 
 | //usage:     "\n	-r ROLE" | 
 | //usage:     "\n	-t TYPE" | 
 | //usage:     "\n	-l RNG" | 
 | //usage:     "\n	-R	Recurse" | 
 | //usage:	) | 
 |  | 
 | #include <selinux/context.h> | 
 |  | 
 | #include "libbb.h" | 
 |  | 
 | #define OPT_RECURSIVE		(1<<0)	/* 'R' */ | 
 | #define OPT_CHANHES		(1<<1)	/* 'c' */ | 
 | #define OPT_NODEREFERENCE	(1<<2)	/* 'h' */ | 
 | #define OPT_QUIET		(1<<3)	/* 'f' */ | 
 | #define OPT_USER		(1<<4)	/* 'u' */ | 
 | #define OPT_ROLE		(1<<5)	/* 'r' */ | 
 | #define OPT_TYPE		(1<<6)	/* 't' */ | 
 | #define OPT_RANGE		(1<<7)	/* 'l' */ | 
 | #define OPT_VERBOSE		(1<<8)	/* 'v' */ | 
 | #define OPT_REFERENCE		((1<<9) * ENABLE_FEATURE_CHCON_LONG_OPTIONS) | 
 | #define OPT_COMPONENT_SPECIFIED	(OPT_USER | OPT_ROLE | OPT_TYPE | OPT_RANGE) | 
 |  | 
 | static char *user = NULL; | 
 | static char *role = NULL; | 
 | static char *type = NULL; | 
 | static char *range = NULL; | 
 | static char *specified_context = NULL; | 
 |  | 
 | static int FAST_FUNC change_filedir_context( | 
 | 		const char *fname, | 
 | 		struct stat *stbuf UNUSED_PARAM, | 
 | 		void *userData UNUSED_PARAM, | 
 | 		int depth UNUSED_PARAM) | 
 | { | 
 | 	context_t context = NULL; | 
 | 	security_context_t file_context = NULL; | 
 | 	security_context_t context_string; | 
 | 	int rc = FALSE; | 
 | 	int status = 0; | 
 |  | 
 | 	if (option_mask32 & OPT_NODEREFERENCE) { | 
 | 		status = lgetfilecon(fname, &file_context); | 
 | 	} else { | 
 | 		status = getfilecon(fname, &file_context); | 
 | 	} | 
 | 	if (status < 0 && errno != ENODATA) { | 
 | 		if ((option_mask32 & OPT_QUIET) == 0) | 
 | 			bb_error_msg("can't obtain security context: %s", fname); | 
 | 		goto skip; | 
 | 	} | 
 |  | 
 | 	if (file_context == NULL && specified_context == NULL) { | 
 | 		bb_error_msg("can't apply partial context to unlabeled file %s", fname); | 
 | 		goto skip; | 
 | 	} | 
 |  | 
 | 	if (specified_context == NULL) { | 
 | 		context = set_security_context_component(file_context, | 
 | 							user, role, type, range); | 
 | 		if (!context) { | 
 | 			bb_error_msg("can't compute security context from %s", file_context); | 
 | 			goto skip; | 
 | 		} | 
 | 	} else { | 
 | 		context = context_new(specified_context); | 
 | 		if (!context) { | 
 | 			bb_error_msg("invalid context: %s", specified_context); | 
 | 			goto skip; | 
 | 		} | 
 | 	} | 
 |  | 
 | 	context_string = context_str(context); | 
 | 	if (!context_string) { | 
 | 		bb_error_msg("can't obtain security context in text expression"); | 
 | 		goto skip; | 
 | 	} | 
 |  | 
 | 	if (file_context == NULL || strcmp(context_string, file_context) != 0) { | 
 | 		int fail; | 
 |  | 
 | 		if (option_mask32 & OPT_NODEREFERENCE) { | 
 | 			fail = lsetfilecon(fname, context_string); | 
 | 		} else { | 
 | 			fail = setfilecon(fname, context_string); | 
 | 		} | 
 | 		if ((option_mask32 & OPT_VERBOSE) || ((option_mask32 & OPT_CHANHES) && !fail)) { | 
 | 			printf(!fail | 
 | 				? "context of %s changed to %s\n" | 
 | 				: "can't change context of %s to %s\n", | 
 | 				fname, context_string); | 
 | 		} | 
 | 		if (!fail) { | 
 | 			rc = TRUE; | 
 | 		} else if ((option_mask32 & OPT_QUIET) == 0) { | 
 | 			bb_error_msg("can't change context of %s to %s", | 
 | 					fname, context_string); | 
 | 		} | 
 | 	} else if (option_mask32 & OPT_VERBOSE) { | 
 | 		printf("context of %s retained as %s\n", fname, context_string); | 
 | 		rc = TRUE; | 
 | 	} | 
 | skip: | 
 | 	context_free(context); | 
 | 	freecon(file_context); | 
 |  | 
 | 	return rc; | 
 | } | 
 |  | 
 | #if ENABLE_FEATURE_CHCON_LONG_OPTIONS | 
 | static const char chcon_longopts[] ALIGN1 = | 
 | 	"recursive\0"      No_argument       "R" | 
 | 	"changes\0"        No_argument       "c" | 
 | 	"no-dereference\0" No_argument       "h" | 
 | 	"silent\0"         No_argument       "f" | 
 | 	"quiet\0"          No_argument       "f" | 
 | 	"user\0"           Required_argument "u" | 
 | 	"role\0"           Required_argument "r" | 
 | 	"type\0"           Required_argument "t" | 
 | 	"range\0"          Required_argument "l" | 
 | 	"verbose\0"        No_argument       "v" | 
 | 	"reference\0"      Required_argument "\xff" /* no short option */ | 
 | 	; | 
 | #endif | 
 |  | 
 | int chcon_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; | 
 | int chcon_main(int argc UNUSED_PARAM, char **argv) | 
 | { | 
 | 	char *reference_file; | 
 | 	char *fname; | 
 | 	int i, errors = 0; | 
 |  | 
 | #if ENABLE_FEATURE_CHCON_LONG_OPTIONS | 
 | 	applet_long_options = chcon_longopts; | 
 | #endif | 
 | 	opt_complementary = "-1"  /* at least 1 param */ | 
 | 		":?"  /* error if exclusivity constraints are violated */ | 
 | #if ENABLE_FEATURE_CHCON_LONG_OPTIONS | 
 | 		":\xff--urtl:u--\xff:r--\xff:t--\xff:l--\xff" | 
 | #endif | 
 | 		":f--v:v--f";  /* 'verbose' and 'quiet' are exclusive */ | 
 | 	getopt32(argv, "Rchfu:r:t:l:v", | 
 | 		&user, &role, &type, &range, &reference_file); | 
 | 	argv += optind; | 
 |  | 
 | #if ENABLE_FEATURE_CHCON_LONG_OPTIONS | 
 | 	if (option_mask32 & OPT_REFERENCE) { | 
 | 		/* FIXME: lgetfilecon() should be used when '-h' is specified. | 
 | 		 * But current implementation follows the original one. */ | 
 | 		if (getfilecon(reference_file, &specified_context) < 0) | 
 | 			bb_perror_msg_and_die("getfilecon('%s') failed", reference_file); | 
 | 	} else | 
 | #endif | 
 | 	if ((option_mask32 & OPT_COMPONENT_SPECIFIED) == 0) { | 
 | 		specified_context = *argv++; | 
 | 		/* specified_context is never NULL - | 
 | 		 * "-1" in opt_complementary prevents this. */ | 
 | 		if (!argv[0]) | 
 | 			bb_error_msg_and_die("too few arguments"); | 
 | 	} | 
 |  | 
 | 	for (i = 0; (fname = argv[i]) != NULL; i++) { | 
 | 		int fname_len = strlen(fname); | 
 | 		while (fname_len > 1 && fname[fname_len - 1] == '/') | 
 | 			fname_len--; | 
 | 		fname[fname_len] = '\0'; | 
 |  | 
 | 		if (recursive_action(fname, | 
 | 					1<<option_mask32 & OPT_RECURSIVE, | 
 | 					change_filedir_context, | 
 | 					change_filedir_context, | 
 | 					NULL, 0) != TRUE) | 
 | 			errors = 1; | 
 | 	} | 
 | 	return errors; | 
 | } |