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