diff options
author | Denis Vlasenko <vda.linux@googlemail.com> | 2007-07-23 14:03:30 +0000 |
---|---|---|
committer | Denis Vlasenko <vda.linux@googlemail.com> | 2007-07-23 14:03:30 +0000 |
commit | 397137b8158298cae14efae330abc57ba11630a5 (patch) | |
tree | 66a755944637f3229f3f801c999132e8d9386856 /selinux | |
parent | 845db2acf3cf7e87c20cee01dc228de6fad593ed (diff) | |
download | busybox-w32-397137b8158298cae14efae330abc57ba11630a5.tar.gz busybox-w32-397137b8158298cae14efae330abc57ba11630a5.tar.bz2 busybox-w32-397137b8158298cae14efae330abc57ba11630a5.zip |
setfiles,restorecon: new SELinux applets by Yuichi Nakamura <ynakam@hitachisoft.jp>
Diffstat (limited to 'selinux')
-rw-r--r-- | selinux/Config.in | 26 | ||||
-rw-r--r-- | selinux/Kbuild | 2 | ||||
-rw-r--r-- | selinux/setfiles.c | 702 |
3 files changed, 730 insertions, 0 deletions
diff --git a/selinux/Config.in b/selinux/Config.in index 42bca66d6..77f89c798 100644 --- a/selinux/Config.in +++ b/selinux/Config.in | |||
@@ -49,6 +49,14 @@ config MATCHPATHCON | |||
49 | Enable support to get default security context of the | 49 | Enable support to get default security context of the |
50 | specified path from the file contexts configuration. | 50 | specified path from the file contexts configuration. |
51 | 51 | ||
52 | config RESTORECON | ||
53 | bool "restorecon" | ||
54 | default n | ||
55 | depends on SELINUX | ||
56 | help | ||
57 | Enable support to relabel files. The feature is almost | ||
58 | the same as setfiles, but usage is a little different. | ||
59 | |||
52 | config RUNCON | 60 | config RUNCON |
53 | bool "runcon" | 61 | bool "runcon" |
54 | default n | 62 | default n |
@@ -78,5 +86,23 @@ config SETENFORCE | |||
78 | help | 86 | help |
79 | Enable support to modify the mode SELinux is running in. | 87 | Enable support to modify the mode SELinux is running in. |
80 | 88 | ||
89 | config SETFILES | ||
90 | bool "setfiles" | ||
91 | default n | ||
92 | depends on SELINUX | ||
93 | help | ||
94 | Enable support to modify to relabel files. | ||
95 | Notice: If you built libselinux with -D_FILE_OFFSET_BITS=64, | ||
96 | (It is default in libselinux's Makefile), you _must_ enable | ||
97 | CONFIG_LFS. | ||
98 | |||
99 | config FEATURE_SETFILES_CHECK_OPTION | ||
100 | bool "Enable check option" | ||
101 | default n | ||
102 | depends on SETFILES | ||
103 | help | ||
104 | Support "-c" option (check the validity of the contexts against | ||
105 | the specified binary policy) for setfiles. Requires libsepol. | ||
106 | |||
81 | endmenu | 107 | endmenu |
82 | 108 | ||
diff --git a/selinux/Kbuild b/selinux/Kbuild index 09cae4d2d..08f49c867 100644 --- a/selinux/Kbuild +++ b/selinux/Kbuild | |||
@@ -14,3 +14,5 @@ lib-$(CONFIG_MATCHPATHCON) += matchpathcon.o | |||
14 | lib-$(CONFIG_RUNCON) += runcon.o | 14 | lib-$(CONFIG_RUNCON) += runcon.o |
15 | lib-$(CONFIG_SELINUXENABLED) += selinuxenabled.o | 15 | lib-$(CONFIG_SELINUXENABLED) += selinuxenabled.o |
16 | lib-$(CONFIG_SETENFORCE) += setenforce.o | 16 | lib-$(CONFIG_SETENFORCE) += setenforce.o |
17 | lib-$(CONFIG_SETFILES) += setfiles.o | ||
18 | lib-$(CONFIG_RESTORECON) += setfiles.o | ||
diff --git a/selinux/setfiles.c b/selinux/setfiles.c new file mode 100644 index 000000000..42207fcfb --- /dev/null +++ b/selinux/setfiles.c | |||
@@ -0,0 +1,702 @@ | |||
1 | /* | ||
2 | setfiles: based on policycoreutils 2.0.19 | ||
3 | policycoreutils was released under GPL 2. | ||
4 | Port to BusyBox by 2007 Yuichi Nakamura <ynakam@hitachisoft.jp> | ||
5 | */ | ||
6 | |||
7 | #include "libbb.h" | ||
8 | #if ENABLE_FEATURE_SETFILES_CHECK_OPTION | ||
9 | #include <sepol/sepol.h> | ||
10 | #endif | ||
11 | |||
12 | #define MAX_EXCLUDES 50 | ||
13 | |||
14 | struct edir { | ||
15 | char *directory; | ||
16 | size_t size; | ||
17 | }; | ||
18 | |||
19 | struct globals { | ||
20 | FILE *outfile; | ||
21 | char *policyfile; | ||
22 | char *rootpath; | ||
23 | int rootpathlen; | ||
24 | unsigned count; | ||
25 | int excludeCtr; | ||
26 | int errors; | ||
27 | int verbose; /* getopt32 uses it, has to be int */ | ||
28 | //smallint force; | ||
29 | //smallint progress; | ||
30 | //smallint debug; | ||
31 | //smallint dry_run; | ||
32 | //smallint quiet; | ||
33 | //smallint ignore_enoent; | ||
34 | //smallint take_log; | ||
35 | //smallint warn_no_match; | ||
36 | smallint recurse; /* Recursive descent */ | ||
37 | smallint follow_mounts; | ||
38 | /* Behavior flags determined based on setfiles vs. restorecon */ | ||
39 | smallint expand_realpath; /* Expand paths via realpath */ | ||
40 | smallint abort_on_error; /* Abort the file tree walk upon an error */ | ||
41 | int add_assoc; /* Track inode associations for conflict detection */ | ||
42 | int matchpathcon_flags; /* Flags to matchpathcon */ | ||
43 | dev_t dev_id; /* Device id where target file exists */ | ||
44 | int nerr; | ||
45 | struct edir excludeArray[MAX_EXCLUDES]; | ||
46 | }; | ||
47 | |||
48 | #define G (*(struct globals*)&bb_common_bufsiz1) | ||
49 | void BUG_setfiles_globals_too_big(void); | ||
50 | #define INIT_G() do { \ | ||
51 | if (sizeof(G) > COMMON_BUFSIZE) \ | ||
52 | BUG_setfiles_globals_too_big(); \ | ||
53 | /* memset(&G, 0, sizeof(G)); - already is */ \ | ||
54 | } while (0) | ||
55 | #define outfile (G.outfile ) | ||
56 | #define policyfile (G.policyfile ) | ||
57 | #define rootpath (G.rootpath ) | ||
58 | #define rootpathlen (G.rootpathlen ) | ||
59 | #define count (G.count ) | ||
60 | #define excludeCtr (G.excludeCtr ) | ||
61 | #define errors (G.errors ) | ||
62 | #define verbose (G.verbose ) | ||
63 | //#define force (G.force ) | ||
64 | //#define progress (G.progress ) | ||
65 | //#define debug (G.debug ) | ||
66 | //#define dry_run (G.dry_run ) | ||
67 | //#define quiet (G.quiet ) | ||
68 | //#define ignore_enoent (G.ignore_enoent ) | ||
69 | //#define take_log (G.take_log ) | ||
70 | //#define warn_no_match (G.warn_no_match ) | ||
71 | #define recurse (G.recurse ) | ||
72 | #define follow_mounts (G.follow_mounts ) | ||
73 | #define expand_realpath (G.expand_realpath ) | ||
74 | #define abort_on_error (G.abort_on_error ) | ||
75 | #define add_assoc (G.add_assoc ) | ||
76 | #define matchpathcon_flags (G.matchpathcon_flags) | ||
77 | #define dev_id (G.dev_id ) | ||
78 | #define nerr (G.nerr ) | ||
79 | #define excludeArray (G.excludeArray ) | ||
80 | |||
81 | /* Must match getopt32 string! */ | ||
82 | enum { | ||
83 | OPT_d = (1 << 0), | ||
84 | OPT_e = (1 << 1), | ||
85 | OPT_f = (1 << 2), | ||
86 | OPT_i = (1 << 3), | ||
87 | OPT_l = (1 << 4), | ||
88 | OPT_n = (1 << 5), | ||
89 | OPT_p = (1 << 6), | ||
90 | OPT_q = (1 << 7), | ||
91 | OPT_r = (1 << 8), | ||
92 | OPT_s = (1 << 9), | ||
93 | OPT_v = (1 << 10), | ||
94 | OPT_o = (1 << 11), | ||
95 | OPT_F = (1 << 12), | ||
96 | OPT_W = (1 << 13), | ||
97 | OPT_c = (1 << 14), /* c only for setfiles */ | ||
98 | OPT_R = (1 << 14), /* R only for restorecon */ | ||
99 | }; | ||
100 | #define FLAG_d_debug (option_mask32 & OPT_d) | ||
101 | #define FLAG_e (option_mask32 & OPT_e) | ||
102 | #define FLAG_f (option_mask32 & OPT_f) | ||
103 | #define FLAG_i_ignore_enoent (option_mask32 & OPT_i) | ||
104 | #define FLAG_l_take_log (option_mask32 & OPT_l) | ||
105 | #define FLAG_n_dry_run (option_mask32 & OPT_n) | ||
106 | #define FLAG_p_progress (option_mask32 & OPT_p) | ||
107 | #define FLAG_q_quiet (option_mask32 & OPT_q) | ||
108 | #define FLAG_r (option_mask32 & OPT_r) | ||
109 | #define FLAG_s (option_mask32 & OPT_s) | ||
110 | #define FLAG_v (option_mask32 & OPT_v) | ||
111 | #define FLAG_o (option_mask32 & OPT_o) | ||
112 | #define FLAG_F_force (option_mask32 & OPT_F) | ||
113 | #define FLAG_W_warn_no_match (option_mask32 & OPT_W) | ||
114 | #define FLAG_c (option_mask32 & OPT_c) | ||
115 | #define FLAG_R (option_mask32 & OPT_R) | ||
116 | |||
117 | |||
118 | static void qprintf(const char *fmt, ...) | ||
119 | { | ||
120 | /* quiet, do nothing */ | ||
121 | } | ||
122 | |||
123 | static void inc_err(void) | ||
124 | { | ||
125 | nerr++; | ||
126 | if (nerr > 9 && !FLAG_d_debug) { | ||
127 | bb_error_msg_and_die("exiting after 10 errors"); | ||
128 | } | ||
129 | } | ||
130 | |||
131 | static int add_exclude(const char *directory) | ||
132 | { | ||
133 | struct stat sb; | ||
134 | size_t len = 0; | ||
135 | |||
136 | if (directory == NULL || directory[0] != '/') { | ||
137 | bb_error_msg("full path required for exclude: %s", directory); | ||
138 | return 1; | ||
139 | } | ||
140 | if (lstat(directory, &sb)) { | ||
141 | bb_error_msg("directory \"%s\" not found, ignoring", directory); | ||
142 | return 0; | ||
143 | } | ||
144 | if ((sb.st_mode & S_IFDIR) == 0) { | ||
145 | bb_error_msg("\"%s\" is not a directory: mode %o, ignoring", | ||
146 | directory, sb.st_mode); | ||
147 | return 0; | ||
148 | } | ||
149 | if (excludeCtr == MAX_EXCLUDES) { | ||
150 | bb_error_msg("maximum excludes %d exceeded", MAX_EXCLUDES); | ||
151 | return 1; | ||
152 | } | ||
153 | |||
154 | len = strlen(directory); | ||
155 | while (len > 1 && directory[len - 1] == '/') { | ||
156 | len--; | ||
157 | } | ||
158 | excludeArray[excludeCtr].directory = xstrndup(directory, len); | ||
159 | excludeArray[excludeCtr++].size = len; | ||
160 | |||
161 | return 0; | ||
162 | } | ||
163 | |||
164 | static int exclude(const char *file) | ||
165 | { | ||
166 | int i = 0; | ||
167 | for (i = 0; i < excludeCtr; i++) { | ||
168 | if (strncmp(file, excludeArray[i].directory, | ||
169 | excludeArray[i].size) == 0) { | ||
170 | if (file[excludeArray[i].size] == '\0' | ||
171 | || file[excludeArray[i].size] == '/') { | ||
172 | return 1; | ||
173 | } | ||
174 | } | ||
175 | } | ||
176 | return 0; | ||
177 | } | ||
178 | |||
179 | static int match(const char *name, struct stat *sb, char **con) | ||
180 | { | ||
181 | int ret; | ||
182 | char path[PATH_MAX + 1]; | ||
183 | char *tmp_path = xstrdup(name); | ||
184 | |||
185 | if (excludeCtr > 0) { | ||
186 | if (exclude(name)) { | ||
187 | goto err; | ||
188 | } | ||
189 | } | ||
190 | ret = lstat(name, sb); | ||
191 | if (ret) { | ||
192 | if (FLAG_i_ignore_enoent && errno == ENOENT) { | ||
193 | free(tmp_path); | ||
194 | return 0; | ||
195 | } | ||
196 | bb_error_msg("stat(%s)", name); | ||
197 | goto err; | ||
198 | } | ||
199 | |||
200 | if (expand_realpath) { | ||
201 | if (S_ISLNK(sb->st_mode)) { | ||
202 | char *p = NULL; | ||
203 | char *file_sep; | ||
204 | |||
205 | size_t len = 0; | ||
206 | |||
207 | if (verbose > 1) | ||
208 | bb_error_msg("warning! %s refers to a symbolic link, not following last component", name); | ||
209 | |||
210 | file_sep = strrchr(tmp_path, '/'); | ||
211 | if (file_sep == tmp_path) { | ||
212 | file_sep++; | ||
213 | p[0] = '\0'; | ||
214 | p = path; | ||
215 | } else if (file_sep) { | ||
216 | *file_sep++ = '\0'; | ||
217 | p = realpath(tmp_path, path); | ||
218 | } else { | ||
219 | file_sep = tmp_path; | ||
220 | p = realpath("./", path); | ||
221 | } | ||
222 | if (p) | ||
223 | len = strlen(p); | ||
224 | if (!p || len + strlen(file_sep) + 2 > PATH_MAX) { | ||
225 | bb_perror_msg("realpath(%s) failed", name); | ||
226 | goto err; | ||
227 | } | ||
228 | p += len; | ||
229 | /* ensure trailing slash of directory name */ | ||
230 | if (len == 0 || p[-1] != '/') { | ||
231 | *p++ = '/'; | ||
232 | } | ||
233 | strcpy(p, file_sep); | ||
234 | name = path; | ||
235 | if (excludeCtr > 0 && exclude(name)) | ||
236 | goto err; | ||
237 | |||
238 | } else { | ||
239 | char *p; | ||
240 | p = realpath(name, path); | ||
241 | if (!p) { | ||
242 | bb_perror_msg("realpath(%s)", name); | ||
243 | goto err; | ||
244 | } | ||
245 | name = p; | ||
246 | if (excludeCtr > 0 && exclude(name)) | ||
247 | goto err; | ||
248 | } | ||
249 | } | ||
250 | |||
251 | /* name will be what is matched in the policy */ | ||
252 | if (NULL != rootpath) { | ||
253 | if (0 != strncmp(rootpath, name, rootpathlen)) { | ||
254 | bb_error_msg("%s is not located in %s", | ||
255 | name, rootpath); | ||
256 | goto err; | ||
257 | } | ||
258 | name += rootpathlen; | ||
259 | } | ||
260 | |||
261 | free(tmp_path); | ||
262 | if (rootpath != NULL && name[0] == '\0') | ||
263 | /* this is actually the root dir of the alt root */ | ||
264 | return matchpathcon_index("/", sb->st_mode, con); | ||
265 | return matchpathcon_index(name, sb->st_mode, con); | ||
266 | err: | ||
267 | free(tmp_path); | ||
268 | return -1; | ||
269 | } | ||
270 | |||
271 | /* Compare two contexts to see if their differences are "significant", | ||
272 | * or whether the only difference is in the user. */ | ||
273 | static int only_changed_user(const char *a, const char *b) | ||
274 | { | ||
275 | if (FLAG_F_force) | ||
276 | return 0; | ||
277 | if (!a || !b) | ||
278 | return 0; | ||
279 | a = strchr(a, ':'); /* Rest of the context after the user */ | ||
280 | b = strchr(b, ':'); | ||
281 | if (!a || !b) | ||
282 | return 0; | ||
283 | return (strcmp(a, b) == 0); | ||
284 | } | ||
285 | |||
286 | static int restore(const char *file) | ||
287 | { | ||
288 | char *my_file = xstrdup(file); | ||
289 | char *my_file_orig = my_file; | ||
290 | struct stat my_sb; | ||
291 | int i, j, ret; | ||
292 | char *context = NULL; | ||
293 | char *newcon = NULL; | ||
294 | int user_only_changed = 0; | ||
295 | size_t len = strlen(my_file); | ||
296 | int retval = 0; | ||
297 | |||
298 | /* Skip the extra slashes at the beginning and end, if present. */ | ||
299 | if (file[0] == '/' && file[1] == '/') | ||
300 | my_file++; | ||
301 | if (len > 1 && my_file[len - 1] == '/') | ||
302 | my_file[len - 1] = '\0'; | ||
303 | |||
304 | i = match(my_file, &my_sb, &newcon); | ||
305 | |||
306 | if (i < 0) /* No matching specification. */ | ||
307 | goto out; | ||
308 | |||
309 | |||
310 | if (FLAG_p_progress) { | ||
311 | count++; | ||
312 | if (count % 0x400 == 0) { /* every 1024 times */ | ||
313 | count = (count % (80*0x400)); | ||
314 | if (count == 0) | ||
315 | fputc('\n', stdout); | ||
316 | fputc('*', stdout); | ||
317 | fflush(stdout); | ||
318 | } | ||
319 | } | ||
320 | |||
321 | /* | ||
322 | * Try to add an association between this inode and | ||
323 | * this specification. If there is already an association | ||
324 | * for this inode and it conflicts with this specification, | ||
325 | * then use the last matching specification. | ||
326 | */ | ||
327 | if (add_assoc) { | ||
328 | j = matchpathcon_filespec_add(my_sb.st_ino, i, my_file); | ||
329 | if (j < 0) | ||
330 | goto err; | ||
331 | |||
332 | if (j != i) { | ||
333 | /* There was already an association and it took precedence. */ | ||
334 | goto out; | ||
335 | } | ||
336 | } | ||
337 | |||
338 | if (FLAG_d_debug) | ||
339 | printf("%s: %s matched by %s\n", applet_name, my_file, newcon); | ||
340 | |||
341 | /* Get the current context of the file. */ | ||
342 | ret = lgetfilecon_raw(my_file, &context); | ||
343 | if (ret < 0) { | ||
344 | if (errno == ENODATA) { | ||
345 | context = NULL; /* paranoia */ | ||
346 | } else { | ||
347 | bb_perror_msg("lgetfilecon_raw on %s", my_file); | ||
348 | goto err; | ||
349 | } | ||
350 | user_only_changed = 0; | ||
351 | } else | ||
352 | user_only_changed = only_changed_user(context, newcon); | ||
353 | |||
354 | /* | ||
355 | * Do not relabel the file if the matching specification is | ||
356 | * <<none>> or the file is already labeled according to the | ||
357 | * specification. | ||
358 | */ | ||
359 | if ((strcmp(newcon, "<<none>>") == 0) | ||
360 | || (context && (strcmp(context, newcon) == 0) && !FLAG_F_force)) { | ||
361 | goto out; | ||
362 | } | ||
363 | |||
364 | if (!FLAG_F_force && context && (is_context_customizable(context) > 0)) { | ||
365 | if (verbose > 1) { | ||
366 | bb_error_msg("skipping %s. %s is customizable_types", | ||
367 | my_file, context); | ||
368 | } | ||
369 | goto out; | ||
370 | } | ||
371 | |||
372 | if (verbose) { | ||
373 | /* If we're just doing "-v", trim out any relabels where | ||
374 | * the user has changed but the role and type are the | ||
375 | * same. For "-vv", emit everything. */ | ||
376 | if (verbose > 1 || !user_only_changed) { | ||
377 | bb_info_msg("%s: reset %s context %s->%s", | ||
378 | applet_name, my_file, context ?: "", newcon); | ||
379 | } | ||
380 | } | ||
381 | |||
382 | if (FLAG_l_take_log && !user_only_changed) { | ||
383 | if (context) | ||
384 | bb_info_msg("relabeling %s from %s to %s", my_file, context, newcon); | ||
385 | else | ||
386 | bb_info_msg("labeling %s to %s", my_file, newcon); | ||
387 | } | ||
388 | |||
389 | if (outfile && !user_only_changed) | ||
390 | fprintf(outfile, "%s\n", my_file); | ||
391 | |||
392 | /* | ||
393 | * Do not relabel the file if -n was used. | ||
394 | */ | ||
395 | if (FLAG_n_dry_run || user_only_changed) | ||
396 | goto out; | ||
397 | |||
398 | /* | ||
399 | * Relabel the file to the specified context. | ||
400 | */ | ||
401 | ret = lsetfilecon(my_file, newcon); | ||
402 | if (ret) { | ||
403 | bb_perror_msg("lsetfileconon(%s,%s)", my_file, newcon); | ||
404 | goto err; | ||
405 | } | ||
406 | |||
407 | out: | ||
408 | freecon(context); | ||
409 | freecon(newcon); | ||
410 | free(my_file_orig); | ||
411 | return retval; | ||
412 | err: | ||
413 | retval--; /* -1 */ | ||
414 | goto out; | ||
415 | } | ||
416 | |||
417 | /* | ||
418 | * Apply the last matching specification to a file. | ||
419 | * This function is called by recursive_action on each file during | ||
420 | * the directory traversal. | ||
421 | */ | ||
422 | static int apply_spec(const char *file, | ||
423 | struct stat *sb, void *userData, int depth) | ||
424 | { | ||
425 | if (!follow_mounts) { | ||
426 | /* setfiles does not process across different mount points */ | ||
427 | if (sb->st_dev != dev_id) { | ||
428 | return SKIP; | ||
429 | } | ||
430 | } | ||
431 | errors |= restore(file); | ||
432 | if (abort_on_error && errors) | ||
433 | return FALSE; | ||
434 | return TRUE; | ||
435 | } | ||
436 | |||
437 | |||
438 | static int canoncon(const char *path, unsigned lineno, char **contextp) | ||
439 | { | ||
440 | static const char err_msg[] = "%s: line %u has invalid context %s"; | ||
441 | |||
442 | char *tmpcon; | ||
443 | char *context = *contextp; | ||
444 | int invalid = 0; | ||
445 | |||
446 | #if ENABLE_FEATURE_SETFILES_CHECK_OPTION | ||
447 | if (policyfile) { | ||
448 | if (sepol_check_context(context) >= 0) | ||
449 | return 0; | ||
450 | /* Exit immediately if we're in checking mode. */ | ||
451 | bb_error_msg_and_die(err_msg, path, lineno, context); | ||
452 | } | ||
453 | #endif | ||
454 | |||
455 | if (security_canonicalize_context_raw(context, &tmpcon) < 0) { | ||
456 | if (errno != ENOENT) { | ||
457 | invalid = 1; | ||
458 | inc_err(); | ||
459 | } | ||
460 | } else { | ||
461 | free(context); | ||
462 | *contextp = tmpcon; | ||
463 | } | ||
464 | |||
465 | if (invalid) { | ||
466 | bb_error_msg(err_msg, path, lineno, context); | ||
467 | } | ||
468 | |||
469 | return invalid; | ||
470 | } | ||
471 | |||
472 | static int process_one(char *name) | ||
473 | { | ||
474 | struct stat sb; | ||
475 | int rc; | ||
476 | |||
477 | rc = lstat(name, &sb); | ||
478 | if (rc < 0) { | ||
479 | if (FLAG_i_ignore_enoent && errno == ENOENT) | ||
480 | return 0; | ||
481 | bb_perror_msg("stat(%s)", name); | ||
482 | goto err; | ||
483 | } | ||
484 | dev_id = sb.st_dev; | ||
485 | |||
486 | if (S_ISDIR(sb.st_mode) && recurse) { | ||
487 | if (recursive_action(name, | ||
488 | ACTION_RECURSE, | ||
489 | apply_spec, | ||
490 | apply_spec, | ||
491 | NULL, 0) != TRUE) { | ||
492 | bb_error_msg("error while labeling %s", name); | ||
493 | goto err; | ||
494 | } | ||
495 | } else { | ||
496 | rc = restore(name); | ||
497 | if (rc) | ||
498 | goto err; | ||
499 | } | ||
500 | |||
501 | out: | ||
502 | if (add_assoc) { | ||
503 | if (FLAG_q_quiet) | ||
504 | set_matchpathcon_printf(&qprintf); | ||
505 | matchpathcon_filespec_eval(); | ||
506 | set_matchpathcon_printf(NULL); | ||
507 | matchpathcon_filespec_destroy(); | ||
508 | } | ||
509 | |||
510 | return rc; | ||
511 | |||
512 | err: | ||
513 | rc = -1; | ||
514 | goto out; | ||
515 | } | ||
516 | |||
517 | int setfiles_main(int argc, char **argv); | ||
518 | int setfiles_main(int argc, char **argv) | ||
519 | { | ||
520 | struct stat sb; | ||
521 | int rc, i = 0; | ||
522 | const char *input_filename = NULL; | ||
523 | int use_input_file = 0; | ||
524 | char *buf = NULL; | ||
525 | size_t buf_len; | ||
526 | int flags; | ||
527 | llist_t *exclude_dir = NULL; | ||
528 | char *out_filename = NULL; | ||
529 | |||
530 | INIT_G(); | ||
531 | |||
532 | if (applet_name[0] == 's') { /* "setfiles" */ | ||
533 | /* | ||
534 | * setfiles: | ||
535 | * Recursive descent, | ||
536 | * Does not expand paths via realpath, | ||
537 | * Aborts on errors during the file tree walk, | ||
538 | * Try to track inode associations for conflict detection, | ||
539 | * Does not follow mounts, | ||
540 | * Validates all file contexts at init time. | ||
541 | */ | ||
542 | recurse = 1; | ||
543 | abort_on_error = 1; | ||
544 | add_assoc = 1; | ||
545 | /* follow_mounts = 0; - already is */ | ||
546 | matchpathcon_flags = MATCHPATHCON_VALIDATE | MATCHPATHCON_NOTRANS; | ||
547 | } else { | ||
548 | /* | ||
549 | * restorecon: | ||
550 | * No recursive descent unless -r/-R, | ||
551 | * Expands paths via realpath, | ||
552 | * Do not abort on errors during the file tree walk, | ||
553 | * Do not try to track inode associations for conflict detection, | ||
554 | * Follows mounts, | ||
555 | * Does lazy validation of contexts upon use. | ||
556 | */ | ||
557 | expand_realpath = 1; | ||
558 | follow_mounts = 1; | ||
559 | matchpathcon_flags = MATCHPATHCON_NOTRANS; | ||
560 | /* restorecon only */ | ||
561 | selinux_or_die(); | ||
562 | } | ||
563 | |||
564 | set_matchpathcon_flags(matchpathcon_flags); | ||
565 | |||
566 | opt_complementary = "e::vv:v--p:p--v"; | ||
567 | /* Option order must match OPT_x definitions! */ | ||
568 | if (applet_name[0] == 'r') { /* restorecon */ | ||
569 | flags = getopt32(argc, argv, "de:f:ilnpqrsvo:FWR", | ||
570 | &exclude_dir, &input_filename, &out_filename, &verbose); | ||
571 | } else { /* setfiles */ | ||
572 | flags = getopt32(argc, argv, "de:f:ilnpqr:svo:FW" | ||
573 | USE_FEATURE_SETFILES_CHECK_OPTION("c:"), | ||
574 | &exclude_dir, &input_filename, &rootpath, &out_filename, | ||
575 | USE_FEATURE_SETFILES_CHECK_OPTION(&policyfile,) | ||
576 | &verbose); | ||
577 | } | ||
578 | |||
579 | #if ENABLE_FEATURE_SETFILES_CHECK_OPTION | ||
580 | if ((applet_name[0] == 's') && (flags & OPT_c)) { | ||
581 | FILE *policystream; | ||
582 | |||
583 | policystream = xfopen(policyfile, "r"); | ||
584 | if (sepol_set_policydb_from_file(policystream) < 0) { | ||
585 | bb_error_msg_and_die("sepol_set_policydb_from_file on %s", policyfile); | ||
586 | } | ||
587 | fclose(policystream); | ||
588 | |||
589 | /* Only process the specified file_contexts file, not | ||
590 | any .homedirs or .local files, and do not perform | ||
591 | context translations. */ | ||
592 | set_matchpathcon_flags(MATCHPATHCON_BASEONLY | | ||
593 | MATCHPATHCON_NOTRANS | | ||
594 | MATCHPATHCON_VALIDATE); | ||
595 | } | ||
596 | #endif | ||
597 | |||
598 | //if (flags & OPT_d) { | ||
599 | // debug = 1; | ||
600 | //} | ||
601 | if (flags & OPT_e) { | ||
602 | if (exclude_dir == NULL) { | ||
603 | bb_show_usage(); | ||
604 | } | ||
605 | while (exclude_dir) { | ||
606 | if (add_exclude(llist_pop(&exclude_dir))) | ||
607 | exit(1); | ||
608 | } | ||
609 | } | ||
610 | //if (flags & OPT_i) { | ||
611 | // ignore_enoent = 1; | ||
612 | //} | ||
613 | //if (flags & OPT_l) { | ||
614 | // take_log = 1; | ||
615 | //} | ||
616 | //if (flags & OPT_F) { | ||
617 | // force = 1; | ||
618 | //} | ||
619 | //if (flags & OPT_n) { | ||
620 | // dry_run = 1; | ||
621 | //} | ||
622 | if (flags & OPT_o) { | ||
623 | outfile = stdout; | ||
624 | if (NOT_LONE_CHAR(out_filename, '-')) { | ||
625 | outfile = xfopen(out_filename, "w"); | ||
626 | } | ||
627 | } | ||
628 | //if (flags & OPT_q) { | ||
629 | // quiet = 1; | ||
630 | //} | ||
631 | if (applet_name[0] == 'r') { /* restorecon */ | ||
632 | if (flags & (OPT_r | OPT_R)) | ||
633 | recurse = 1; | ||
634 | } else { /* setfiles */ | ||
635 | if (flags & OPT_r) | ||
636 | rootpathlen = strlen(rootpath); | ||
637 | } | ||
638 | if (flags & OPT_s) { | ||
639 | use_input_file = 1; | ||
640 | input_filename = "-"; | ||
641 | add_assoc = 0; | ||
642 | } | ||
643 | //if (flags & OPT_p) { | ||
644 | // progress = 1; | ||
645 | //} | ||
646 | //if (flags & OPT_W) { | ||
647 | // warn_no_match = 1; | ||
648 | //} | ||
649 | |||
650 | if (applet_name[0] == 's') { /* setfiles */ | ||
651 | /* Use our own invalid context checking function so that | ||
652 | we can support either checking against the active policy or | ||
653 | checking against a binary policy file. */ | ||
654 | set_matchpathcon_canoncon(&canoncon); | ||
655 | if (argc == 1) | ||
656 | bb_show_usage(); | ||
657 | if (stat(argv[optind], &sb) < 0) { | ||
658 | bb_perror_msg_and_die("%s", argv[optind]); | ||
659 | } | ||
660 | if (!S_ISREG(sb.st_mode)) { | ||
661 | bb_error_msg_and_die("spec file %s is not a regular file", argv[optind]); | ||
662 | } | ||
663 | /* Load the file contexts configuration and check it. */ | ||
664 | rc = matchpathcon_init(argv[optind]); | ||
665 | if (rc < 0) { | ||
666 | bb_perror_msg_and_die("%s", argv[optind]); | ||
667 | } | ||
668 | |||
669 | optind++; | ||
670 | |||
671 | if (nerr) | ||
672 | exit(1); | ||
673 | } | ||
674 | |||
675 | if (use_input_file) { | ||
676 | ssize_t len; | ||
677 | FILE *f = stdin; | ||
678 | |||
679 | if (NOT_LONE_CHAR(input_filename, '-')) | ||
680 | f = xfopen(input_filename, "r"); | ||
681 | while ((len = getline(&buf, &buf_len, f)) > 0) { | ||
682 | buf[len - 1] = '\0'; | ||
683 | errors |= process_one(buf); | ||
684 | } | ||
685 | if (ENABLE_FEATURE_CLEAN_UP) | ||
686 | fclose_if_not_stdin(f); | ||
687 | } else { | ||
688 | if (optind >= argc) | ||
689 | bb_show_usage(); | ||
690 | for (i = optind; i < argc; i++) { | ||
691 | errors |= process_one(argv[i]); | ||
692 | } | ||
693 | } | ||
694 | |||
695 | if (FLAG_W_warn_no_match) | ||
696 | matchpathcon_checkmatches(argv[0]); | ||
697 | |||
698 | if (ENABLE_FEATURE_CLEAN_UP && outfile) | ||
699 | fclose(outfile); | ||
700 | |||
701 | return errors; | ||
702 | } | ||