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