Big cleanup in config help and description
[oweals/busybox.git] / selinux / chcon.c
1 /*
2  * chcon -- change security context, based on coreutils-5.97-13
3  *
4  * Port to busybox: KaiGai Kohei <kaigai@kaigai.gr.jp>
5  *
6  * Copyright (C) 2006 - 2007 KaiGai Kohei <kaigai@kaigai.gr.jp>
7  *
8  * Licensed under GPLv2, see file LICENSE in this source tree.
9  */
10 //config:config CHCON
11 //config:       bool "chcon"
12 //config:       default n
13 //config:       depends on SELINUX
14 //config:       help
15 //config:         Enable support to change the security context of file.
16 //config:
17 //config:config FEATURE_CHCON_LONG_OPTIONS
18 //config:       bool "Enable long options"
19 //config:       default y
20 //config:       depends on CHCON && LONG_OPTS
21
22 //applet:IF_CHCON(APPLET(chcon, BB_DIR_USR_BIN, BB_SUID_DROP))
23
24 //kbuild:lib-$(CONFIG_CHCON) += chcon.o
25
26 //usage:#define chcon_trivial_usage
27 //usage:       "[OPTIONS] CONTEXT FILE..."
28 //usage:       "\n      chcon [OPTIONS] [-u USER] [-r ROLE] [-l RANGE] [-t TYPE] FILE..."
29 //usage:        IF_FEATURE_CHCON_LONG_OPTIONS(
30 //usage:       "\n      chcon [OPTIONS] --reference=RFILE FILE..."
31 //usage:        )
32 //usage:#define chcon_full_usage "\n\n"
33 //usage:       "Change the security context of each FILE to CONTEXT\n"
34 //usage:        IF_FEATURE_CHCON_LONG_OPTIONS(
35 //usage:     "\n        -v,--verbose            Verbose"
36 //usage:     "\n        -c,--changes            Report changes made"
37 //usage:     "\n        -h,--no-dereference     Affect symlinks instead of their targets"
38 //usage:     "\n        -f,--silent,--quiet     Suppress most error messages"
39 //usage:     "\n        --reference=RFILE       Use RFILE's group instead of using a CONTEXT value"
40 //usage:     "\n        -u,--user=USER          Set user/role/type/range in the target"
41 //usage:     "\n        -r,--role=ROLE          security context"
42 //usage:     "\n        -t,--type=TYPE"
43 //usage:     "\n        -l,--range=RANGE"
44 //usage:     "\n        -R,--recursive          Recurse"
45 //usage:        )
46 //usage:        IF_NOT_FEATURE_CHCON_LONG_OPTIONS(
47 //usage:     "\n        -v      Verbose"
48 //usage:     "\n        -c      Report changes made"
49 //usage:     "\n        -h      Affect symlinks instead of their targets"
50 //usage:     "\n        -f      Suppress most error messages"
51 //usage:     "\n        -u USER Set user/role/type/range in the target security context"
52 //usage:     "\n        -r ROLE"
53 //usage:     "\n        -t TYPE"
54 //usage:     "\n        -l RNG"
55 //usage:     "\n        -R      Recurse"
56 //usage:        )
57
58 #include <selinux/context.h>
59
60 #include "libbb.h"
61
62 #define OPT_RECURSIVE           (1<<0)  /* 'R' */
63 #define OPT_CHANHES             (1<<1)  /* 'c' */
64 #define OPT_NODEREFERENCE       (1<<2)  /* 'h' */
65 #define OPT_QUIET               (1<<3)  /* 'f' */
66 #define OPT_USER                (1<<4)  /* 'u' */
67 #define OPT_ROLE                (1<<5)  /* 'r' */
68 #define OPT_TYPE                (1<<6)  /* 't' */
69 #define OPT_RANGE               (1<<7)  /* 'l' */
70 #define OPT_VERBOSE             (1<<8)  /* 'v' */
71 #define OPT_REFERENCE           ((1<<9) * ENABLE_FEATURE_CHCON_LONG_OPTIONS)
72 #define OPT_COMPONENT_SPECIFIED (OPT_USER | OPT_ROLE | OPT_TYPE | OPT_RANGE)
73
74 static char *user = NULL;
75 static char *role = NULL;
76 static char *type = NULL;
77 static char *range = NULL;
78 static char *specified_context = NULL;
79
80 static int FAST_FUNC change_filedir_context(
81                 const char *fname,
82                 struct stat *stbuf UNUSED_PARAM,
83                 void *userData UNUSED_PARAM,
84                 int depth UNUSED_PARAM)
85 {
86         context_t context = NULL;
87         security_context_t file_context = NULL;
88         security_context_t context_string;
89         int rc = FALSE;
90         int status = 0;
91
92         if (option_mask32 & OPT_NODEREFERENCE) {
93                 status = lgetfilecon(fname, &file_context);
94         } else {
95                 status = getfilecon(fname, &file_context);
96         }
97         if (status < 0 && errno != ENODATA) {
98                 if ((option_mask32 & OPT_QUIET) == 0)
99                         bb_error_msg("can't obtain security context: %s", fname);
100                 goto skip;
101         }
102
103         if (file_context == NULL && specified_context == NULL) {
104                 bb_error_msg("can't apply partial context to unlabeled file %s", fname);
105                 goto skip;
106         }
107
108         if (specified_context == NULL) {
109                 context = set_security_context_component(file_context,
110                                                         user, role, type, range);
111                 if (!context) {
112                         bb_error_msg("can't compute security context from %s", file_context);
113                         goto skip;
114                 }
115         } else {
116                 context = context_new(specified_context);
117                 if (!context) {
118                         bb_error_msg("invalid context: %s", specified_context);
119                         goto skip;
120                 }
121         }
122
123         context_string = context_str(context);
124         if (!context_string) {
125                 bb_error_msg("can't obtain security context in text expression");
126                 goto skip;
127         }
128
129         if (file_context == NULL || strcmp(context_string, file_context) != 0) {
130                 int fail;
131
132                 if (option_mask32 & OPT_NODEREFERENCE) {
133                         fail = lsetfilecon(fname, context_string);
134                 } else {
135                         fail = setfilecon(fname, context_string);
136                 }
137                 if ((option_mask32 & OPT_VERBOSE) || ((option_mask32 & OPT_CHANHES) && !fail)) {
138                         printf(!fail
139                                 ? "context of %s changed to %s\n"
140                                 : "can't change context of %s to %s\n",
141                                 fname, context_string);
142                 }
143                 if (!fail) {
144                         rc = TRUE;
145                 } else if ((option_mask32 & OPT_QUIET) == 0) {
146                         bb_error_msg("can't change context of %s to %s",
147                                         fname, context_string);
148                 }
149         } else if (option_mask32 & OPT_VERBOSE) {
150                 printf("context of %s retained as %s\n", fname, context_string);
151                 rc = TRUE;
152         }
153 skip:
154         context_free(context);
155         freecon(file_context);
156
157         return rc;
158 }
159
160 #if ENABLE_FEATURE_CHCON_LONG_OPTIONS
161 static const char chcon_longopts[] ALIGN1 =
162         "recursive\0"      No_argument       "R"
163         "changes\0"        No_argument       "c"
164         "no-dereference\0" No_argument       "h"
165         "silent\0"         No_argument       "f"
166         "quiet\0"          No_argument       "f"
167         "user\0"           Required_argument "u"
168         "role\0"           Required_argument "r"
169         "type\0"           Required_argument "t"
170         "range\0"          Required_argument "l"
171         "verbose\0"        No_argument       "v"
172         "reference\0"      Required_argument "\xff" /* no short option */
173         ;
174 #endif
175
176 int chcon_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
177 int chcon_main(int argc UNUSED_PARAM, char **argv)
178 {
179         char *reference_file;
180         char *fname;
181         int i, errors = 0;
182
183 #if ENABLE_FEATURE_CHCON_LONG_OPTIONS
184         applet_long_options = chcon_longopts;
185 #endif
186         opt_complementary = "-1"  /* at least 1 param */
187                 ":?"  /* error if exclusivity constraints are violated */
188 #if ENABLE_FEATURE_CHCON_LONG_OPTIONS
189                 ":\xff--urtl:u--\xff:r--\xff:t--\xff:l--\xff"
190 #endif
191                 ":f--v:v--f";  /* 'verbose' and 'quiet' are exclusive */
192         getopt32(argv, "Rchfu:r:t:l:v",
193                 &user, &role, &type, &range, &reference_file);
194         argv += optind;
195
196 #if ENABLE_FEATURE_CHCON_LONG_OPTIONS
197         if (option_mask32 & OPT_REFERENCE) {
198                 /* FIXME: lgetfilecon() should be used when '-h' is specified.
199                  * But current implementation follows the original one. */
200                 if (getfilecon(reference_file, &specified_context) < 0)
201                         bb_perror_msg_and_die("getfilecon('%s') failed", reference_file);
202         } else
203 #endif
204         if ((option_mask32 & OPT_COMPONENT_SPECIFIED) == 0) {
205                 specified_context = *argv++;
206                 /* specified_context is never NULL -
207                  * "-1" in opt_complementary prevents this. */
208                 if (!argv[0])
209                         bb_error_msg_and_die("too few arguments");
210         }
211
212         for (i = 0; (fname = argv[i]) != NULL; i++) {
213                 int fname_len = strlen(fname);
214                 while (fname_len > 1 && fname[fname_len - 1] == '/')
215                         fname_len--;
216                 fname[fname_len] = '\0';
217
218                 if (recursive_action(fname,
219                                         1<<option_mask32 & OPT_RECURSIVE,
220                                         change_filedir_context,
221                                         change_filedir_context,
222                                         NULL, 0) != TRUE)
223                         errors = 1;
224         }
225         return errors;
226 }