2
setfiles: based on policycoreutils 2.0.19
3
policycoreutils was released under GPL 2.
4
Port to BusyBox (c) 2007 by Yuichi Nakamura <ynakam@hitachisoft.jp>
6
//config:config SETFILES
7
//config: bool "setfiles (13 kb)"
9
//config: depends on SELINUX
11
//config: Enable support to modify to relabel files.
12
//config: Notice: If you built libselinux with -D_FILE_OFFSET_BITS=64,
13
//config: (It is default in libselinux's Makefile), you _must_ enable
16
//config:config FEATURE_SETFILES_CHECK_OPTION
17
//config: bool "Enable check option"
19
//config: depends on SETFILES
21
//config: Support "-c" option (check the validity of the contexts against
22
//config: the specified binary policy) for setfiles. Requires libsepol.
24
//config:config RESTORECON
25
//config: bool "restorecon (12 kb)"
27
//config: depends on SELINUX
29
//config: Enable support to relabel files. The feature is almost
30
//config: the same as setfiles, but usage is a little different.
32
//applet:IF_SETFILES(APPLET(setfiles, BB_DIR_SBIN, BB_SUID_DROP))
33
// APPLET_ODDNAME:name main location suid_type help
34
//applet:IF_RESTORECON(APPLET_ODDNAME(restorecon, setfiles, BB_DIR_SBIN, BB_SUID_DROP, restorecon))
36
//kbuild:lib-$(CONFIG_SETFILES) += setfiles.o
37
//kbuild:lib-$(CONFIG_RESTORECON) += setfiles.o
39
//usage:#define setfiles_trivial_usage
40
//usage: "[-dnpqsvW] [-e DIR]... [-o FILE] [-r alt_root_path]"
41
//usage: IF_FEATURE_SETFILES_CHECK_OPTION(
42
//usage: " [-c policyfile] spec_file"
45
//usage:#define setfiles_full_usage "\n\n"
46
//usage: "Reset file contexts under pathname according to spec_file\n"
47
//usage: IF_FEATURE_SETFILES_CHECK_OPTION(
48
//usage: "\n -c FILE Check the validity of the contexts against the specified binary policy"
50
//usage: "\n -d Show which specification matched each file"
51
//usage: "\n -l Log changes in file labels to syslog"
52
//TODO: log to syslog is not yet implemented, it goes to stdout only now
53
//usage: "\n -n Don't change any file labels"
54
//usage: "\n -q Suppress warnings"
55
//usage: "\n -r DIR Use an alternate root path"
56
//usage: "\n -e DIR Exclude DIR"
57
//usage: "\n -F Force reset of context to match file_context for customizable files"
58
//usage: "\n -o FILE Save list of files with incorrect context"
59
//usage: "\n -s Take a list of files from stdin (instead of command line)"
60
//usage: "\n -v Show changes in file labels, if type or role are changing"
61
//usage: "\n -vv Show changes in file labels, if type, role, or user are changing"
62
//usage: "\n -W Display warnings about entries that had no matching files"
64
//usage:#define restorecon_trivial_usage
65
//usage: "[-iFnRv] [-e EXCLUDEDIR]... [-o FILE] [-f FILE]"
66
//usage:#define restorecon_full_usage "\n\n"
67
//usage: "Reset security contexts of files in pathname\n"
68
//usage: "\n -i Ignore files that don't exist"
69
//usage: "\n -f FILE File with list of files to process"
70
//usage: "\n -e DIR Directory to exclude"
71
//usage: "\n -R,-r Recurse"
72
//usage: "\n -n Don't change any file labels"
73
//usage: "\n -o FILE Save list of files with incorrect context"
74
//usage: "\n -v Verbose"
75
//usage: "\n -vv Show changed labels"
76
//usage: "\n -F Force reset of context to match file_context"
77
//usage: "\n for customizable files, or the user section,"
78
//usage: "\n if it has changed"
81
#include "common_bufsiz.h"
82
#if ENABLE_FEATURE_SETFILES_CHECK_OPTION
83
#include <sepol/sepol.h>
86
#define MAX_EXCLUDES 50
101
int verbose; /* getopt32 uses it, has to be int */
102
smallint recurse; /* Recursive descent */
103
smallint follow_mounts;
104
/* Behavior flags determined based on setfiles vs. restorecon */
105
smallint expand_realpath; /* Expand paths via realpath */
106
smallint abort_on_error; /* Abort the file tree walk upon an error */
107
int add_assoc; /* Track inode associations for conflict detection */
108
int matchpathcon_flags; /* Flags to matchpathcon */
109
dev_t dev_id; /* Device id where target file exists */
111
struct edir excludeArray[MAX_EXCLUDES];
113
#define G (*(struct globals*)bb_common_bufsiz1)
114
void BUG_setfiles_globals_too_big(void);
115
#define INIT_G() do { \
116
setup_common_bufsiz(); \
117
if (sizeof(G) > COMMON_BUFSIZE) \
118
BUG_setfiles_globals_too_big(); \
119
/* memset(&G, 0, sizeof(G)); - already is */ \
121
#define outfile (G.outfile )
122
#define policyfile (G.policyfile )
123
#define rootpath (G.rootpath )
124
#define rootpathlen (G.rootpathlen )
125
#define count (G.count )
126
#define excludeCtr (G.excludeCtr )
127
#define errors (G.errors )
128
#define verbose (G.verbose )
129
#define recurse (G.recurse )
130
#define follow_mounts (G.follow_mounts )
131
#define expand_realpath (G.expand_realpath )
132
#define abort_on_error (G.abort_on_error )
133
#define add_assoc (G.add_assoc )
134
#define matchpathcon_flags (G.matchpathcon_flags)
135
#define dev_id (G.dev_id )
136
#define nerr (G.nerr )
137
#define excludeArray (G.excludeArray )
139
/* Must match getopt32 string! */
155
OPT_c = (1 << 14), /* c only for setfiles */
156
OPT_R = (1 << 14), /* R only for restorecon */
158
#define FLAG_d_debug (option_mask32 & OPT_d)
159
#define FLAG_e (option_mask32 & OPT_e)
160
#define FLAG_f (option_mask32 & OPT_f)
161
#define FLAG_i_ignore_enoent (option_mask32 & OPT_i)
162
#define FLAG_l_take_log (option_mask32 & OPT_l)
163
#define FLAG_n_dry_run (option_mask32 & OPT_n)
164
#define FLAG_p_progress (option_mask32 & OPT_p)
165
#define FLAG_q_quiet (option_mask32 & OPT_q)
166
#define FLAG_r (option_mask32 & OPT_r)
167
#define FLAG_s (option_mask32 & OPT_s)
168
#define FLAG_v (option_mask32 & OPT_v)
169
#define FLAG_o (option_mask32 & OPT_o)
170
#define FLAG_F_force (option_mask32 & OPT_F)
171
#define FLAG_W_warn_no_match (option_mask32 & OPT_W)
172
#define FLAG_c (option_mask32 & OPT_c)
173
#define FLAG_R (option_mask32 & OPT_R)
176
static void qprintf(const char *fmt UNUSED_PARAM, ...)
178
/* quiet, do nothing */
181
static void inc_err(void)
184
if (nerr > 9 && !FLAG_d_debug) {
185
bb_simple_error_msg_and_die("exiting after 10 errors");
189
static void add_exclude(const char *directory)
194
if (directory == NULL || directory[0] != '/') {
195
bb_error_msg_and_die("full path required for exclude: %s", directory);
197
if (lstat(directory, &sb)) {
198
bb_error_msg("directory \"%s\" not found, ignoring", directory);
201
if ((sb.st_mode & S_IFDIR) == 0) {
202
bb_error_msg("\"%s\" is not a directory: mode %o, ignoring",
203
directory, sb.st_mode);
206
if (excludeCtr == MAX_EXCLUDES) {
207
bb_error_msg_and_die("maximum excludes %d exceeded", MAX_EXCLUDES);
210
len = strlen(directory);
211
while (len > 1 && directory[len - 1] == '/') {
214
excludeArray[excludeCtr].directory = xstrndup(directory, len);
215
excludeArray[excludeCtr++].size = len;
218
static bool exclude(const char *file)
221
for (i = 0; i < excludeCtr; i++) {
222
if (strncmp(file, excludeArray[i].directory,
223
excludeArray[i].size) == 0) {
224
if (file[excludeArray[i].size] == '\0'
225
|| file[excludeArray[i].size] == '/') {
233
static int match(const char *name, struct stat *sb, char **con)
236
char path[PATH_MAX + 1];
237
char *tmp_path = xstrdup(name);
239
if (excludeCtr > 0 && exclude(name)) {
242
ret = lstat(name, sb);
244
if (FLAG_i_ignore_enoent && errno == ENOENT) {
248
bb_error_msg("stat(%s)", name);
252
if (expand_realpath) {
253
if (S_ISLNK(sb->st_mode)) {
260
bb_error_msg("warning! %s refers to a symbolic link, not following last component", name);
262
file_sep = strrchr(tmp_path, '/');
263
if (file_sep == tmp_path) {
267
} else if (file_sep) {
269
p = realpath(tmp_path, path);
272
p = realpath("./", path);
276
if (!p || len + strlen(file_sep) + 2 > PATH_MAX) {
277
bb_perror_msg("realpath(%s) failed", name);
281
/* ensure trailing slash of directory name */
282
if (len == 0 || p[-1] != '/') {
287
if (excludeCtr > 0 && exclude(name))
291
p = realpath(name, path);
293
bb_perror_msg("realpath(%s)", name);
297
if (excludeCtr > 0 && exclude(name))
302
/* name will be what is matched in the policy */
303
if (NULL != rootpath) {
304
if (0 != strncmp(rootpath, name, rootpathlen)) {
305
bb_error_msg("%s is not located in %s",
313
if (rootpath != NULL && name[0] == '\0')
314
/* this is actually the root dir of the alt root */
315
return matchpathcon_index("/", sb->st_mode, con);
316
return matchpathcon_index(name, sb->st_mode, con);
322
/* Compare two contexts to see if their differences are "significant",
323
* or whether the only difference is in the user. */
324
static bool only_changed_user(const char *a, const char *b)
330
a = strchr(a, ':'); /* Rest of the context after the user */
334
return (strcmp(a, b) == 0);
337
static int restore(const char *file)
342
char *context = NULL;
344
bool user_only_changed = 0;
347
my_file = bb_simplify_path(file);
349
i = match(my_file, &my_sb, &newcon);
351
if (i < 0) /* No matching specification. */
354
if (FLAG_p_progress) {
356
if (count % 0x400 == 0) { /* every 1024 times */
357
count = (count % (80*0x400));
366
* Try to add an association between this inode and
367
* this specification. If there is already an association
368
* for this inode and it conflicts with this specification,
369
* then use the last matching specification.
372
j = matchpathcon_filespec_add(my_sb.st_ino, i, my_file);
377
/* There was already an association and it took precedence. */
383
printf("%s: %s matched by %s\n", applet_name, my_file, newcon);
385
/* Get the current context of the file. */
386
ret = lgetfilecon_raw(my_file, &context);
388
if (errno == ENODATA) {
389
context = NULL; /* paranoia */
391
bb_perror_msg("lgetfilecon_raw on %s", my_file);
394
user_only_changed = 0;
396
user_only_changed = only_changed_user(context, newcon);
399
* Do not relabel the file if the matching specification is
400
* <<none>> or the file is already labeled according to the
403
if ((strcmp(newcon, "<<none>>") == 0)
404
|| (context && (strcmp(context, newcon) == 0) && !FLAG_F_force)) {
408
if (!FLAG_F_force && context && (is_context_customizable(context) > 0)) {
410
bb_error_msg("skipping %s. %s is customizable_types",
417
/* If we're just doing "-v", trim out any relabels where
418
* the user has changed but the role and type are the
419
* same. For "-vv", emit everything. */
420
if (verbose > 1 || !user_only_changed) {
421
printf("%s: reset %s context %s->%s\n",
422
applet_name, my_file, context ? context : "", newcon);
426
if (FLAG_l_take_log && !user_only_changed) {
428
printf("relabeling %s from %s to %s\n", my_file, context, newcon);
430
printf("labeling %s to %s\n", my_file, newcon);
433
if (outfile && !user_only_changed)
434
fprintf(outfile, "%s\n", my_file);
437
* Do not relabel the file if -n was used.
439
if (FLAG_n_dry_run || user_only_changed)
443
* Relabel the file to the specified context.
445
ret = lsetfilecon(my_file, newcon);
447
bb_perror_msg("lsetfileconon(%s,%s)", my_file, newcon);
462
* Apply the last matching specification to a file.
463
* This function is called by recursive_action on each file during
464
* the directory traversal.
466
static int FAST_FUNC apply_spec(struct recursive_state *state UNUSED_PARAM,
470
if (!follow_mounts) {
471
/* setfiles does not process across different mount points */
472
if (sb->st_dev != dev_id) {
476
errors |= restore(file);
477
if (abort_on_error && errors)
483
static int canoncon(const char *path, unsigned lineno, char **contextp)
485
static const char err_msg[] ALIGN1 = "%s: line %u has invalid context %s";
488
char *context = *contextp;
491
#if ENABLE_FEATURE_SETFILES_CHECK_OPTION
493
if (sepol_check_context(context) >= 0)
495
/* Exit immediately if we're in checking mode. */
496
bb_error_msg_and_die(err_msg, path, lineno, context);
500
if (security_canonicalize_context_raw(context, &tmpcon) < 0) {
501
if (errno != ENOENT) {
511
bb_error_msg(err_msg, path, lineno, context);
517
static int process_one(char *name)
522
rc = lstat(name, &sb);
524
if (FLAG_i_ignore_enoent && errno == ENOENT)
526
bb_perror_msg("stat(%s)", name);
531
if (S_ISDIR(sb.st_mode) && recurse) {
532
if (recursive_action(name,
538
bb_error_msg("error while labeling %s", name);
550
set_matchpathcon_printf(&qprintf);
551
matchpathcon_filespec_eval();
552
set_matchpathcon_printf(NULL);
553
matchpathcon_filespec_destroy();
563
int setfiles_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
564
int setfiles_main(int argc UNUSED_PARAM, char **argv)
568
const char *input_filename = NULL;
572
llist_t *exclude_dir = NULL;
573
char *out_filename = NULL;
577
if (applet_name[0] == 's') { /* "setfiles" */
581
* Does not expand paths via realpath,
582
* Aborts on errors during the file tree walk,
583
* Try to track inode associations for conflict detection,
584
* Does not follow mounts,
585
* Validates all file contexts at init time.
590
/* follow_mounts = 0; - already is */
591
matchpathcon_flags = MATCHPATHCON_VALIDATE | MATCHPATHCON_NOTRANS;
595
* No recursive descent unless -r/-R,
596
* Expands paths via realpath,
597
* Do not abort on errors during the file tree walk,
598
* Do not try to track inode associations for conflict detection,
600
* Does lazy validation of contexts upon use.
604
matchpathcon_flags = MATCHPATHCON_NOTRANS;
605
/* restorecon only */
609
set_matchpathcon_flags(matchpathcon_flags);
611
/* Option order must match OPT_x definitions! */
612
if (applet_name[0] == 'r') { /* restorecon */
613
flags = getopt32(argv, "^"
614
"de:*f:ilnpqrsvo:FWR"
616
"vv:v--p:p--v:v--q:q--v",
617
&exclude_dir, &input_filename, &out_filename,
620
} else { /* setfiles */
621
flags = getopt32(argv, "^"
622
"de:*f:ilnpqr:svo:FW"
623
IF_FEATURE_SETFILES_CHECK_OPTION("c:")
625
"vv:v--p:p--v:v--q:q--v",
626
&exclude_dir, &input_filename, &rootpath, &out_filename,
627
IF_FEATURE_SETFILES_CHECK_OPTION(&policyfile,)
633
#if ENABLE_FEATURE_SETFILES_CHECK_OPTION
634
if ((applet_name[0] == 's') && (flags & OPT_c)) {
637
policystream = xfopen_for_read(policyfile);
638
if (sepol_set_policydb_from_file(policystream) < 0) {
639
bb_error_msg_and_die("sepol_set_policydb_from_file on %s", policyfile);
641
fclose(policystream);
643
/* Only process the specified file_contexts file, not
644
* any .homedirs or .local files, and do not perform
645
* context translations. */
646
set_matchpathcon_flags(MATCHPATHCON_BASEONLY |
647
MATCHPATHCON_NOTRANS |
648
MATCHPATHCON_VALIDATE);
653
add_exclude(llist_pop(&exclude_dir));
657
if (NOT_LONE_CHAR(out_filename, '-')) {
658
outfile = xfopen_for_write(out_filename);
661
if (applet_name[0] == 'r') { /* restorecon */
662
if (flags & (OPT_r | OPT_R))
664
} else { /* setfiles */
666
rootpathlen = strlen(rootpath);
669
input_filename = "-";
673
if (applet_name[0] == 's') { /* setfiles */
674
/* Use our own invalid context checking function so that
675
* we can support either checking against the active policy or
676
* checking against a binary policy file. */
677
set_matchpathcon_canoncon(&canoncon);
681
if (!S_ISREG(sb.st_mode)) {
682
bb_error_msg_and_die("'%s' is not a regular file", argv[0]);
684
/* Load the file contexts configuration and check it. */
685
rc = matchpathcon_init(argv[0]);
687
bb_simple_perror_msg_and_die(argv[0]);
694
if (input_filename) {
698
if (NOT_LONE_CHAR(input_filename, '-'))
699
f = xfopen_for_read(input_filename);
700
while ((len = getline(&buf, &buf_len, f)) > 0) {
702
errors |= process_one(buf);
704
if (ENABLE_FEATURE_CLEAN_UP)
705
fclose_if_not_stdin(f);
709
for (i = 0; argv[i]; i++) {
710
errors |= process_one(argv[i]);
714
if (FLAG_W_warn_no_match)
715
matchpathcon_checkmatches(argv[0]);
717
if (ENABLE_FEATURE_CLEAN_UP && outfile)