1 #include <getopt.h>
2 #include <stdbool.h>
3 #include <stdio.h>
4 #include <stdlib.h>
5 #include <string.h>
6 #include <unistd.h>
7 #include <sepol/module.h>
8 #include <sepol/policydb/policydb.h>
9 #include <sepol/sepol.h>
10 #include <selinux/context.h>
11 #include <selinux/selinux.h>
12 #include <selinux/label.h>
13 #include <sys/stat.h>
14 #include <sys/types.h>
15
16 static const char * const CHECK_FC_ASSERT_ATTRS[] = { "fs_type", "dev_type", "file_type", NULL };
17 static const char * const CHECK_PC_ASSERT_ATTRS[] = { "property_type", NULL };
18 static const char * const CHECK_SC_ASSERT_ATTRS[] = { "service_manager_type", NULL };
19 static const char * const CHECK_HW_SC_ASSERT_ATTRS[] = { "hwservice_manager_type", NULL };
20 static const char * const CHECK_VND_SC_ASSERT_ATTRS[] = { "vndservice_manager_type", NULL };
21
22 typedef enum filemode filemode;
23 enum filemode {
24 filemode_file_contexts = 0,
25 filemode_property_contexts,
26 filemode_service_contexts,
27 filemode_hw_service_contexts,
28 filemode_vendor_service_contexts
29 };
30
31 static struct {
32 /* policy */
33 struct {
34 union {
35 /* Union these so we don't have to cast */
36 sepol_policydb_t *sdb;
37 policydb_t *pdb;
38 };
39 sepol_policy_file_t *pf;
40 sepol_handle_t *handle;
41 FILE *file;
42 #define SEHANDLE_CNT 2
43 struct selabel_handle *sehnd[SEHANDLE_CNT];
44 } sepolicy;
45
46 /* assertions */
47 struct {
48 const char * const *attrs; /* for the original set to print on error */
49 ebitmap_t set; /* the ebitmap representation of the attrs */
50 } assert;
51
52 } global_state;
53
filemode_to_assert_attrs(filemode mode)54 static const char * const *filemode_to_assert_attrs(filemode mode)
55 {
56 switch (mode) {
57 case filemode_file_contexts:
58 return CHECK_FC_ASSERT_ATTRS;
59 case filemode_property_contexts:
60 return CHECK_PC_ASSERT_ATTRS;
61 case filemode_service_contexts:
62 return CHECK_SC_ASSERT_ATTRS;
63 case filemode_hw_service_contexts:
64 return CHECK_HW_SC_ASSERT_ATTRS;
65 case filemode_vendor_service_contexts:
66 return CHECK_VND_SC_ASSERT_ATTRS;
67 }
68 /* die on invalid parameters */
69 fprintf(stderr, "Error: Invalid mode of operation: %d\n", mode);
70 exit(1);
71 }
72
get_attr_bit(policydb_t * policydb,const char * attr_name)73 static int get_attr_bit(policydb_t *policydb, const char *attr_name)
74 {
75 struct type_datum *attr = hashtab_search(policydb->p_types.table, (char *)attr_name);
76 if (!attr) {
77 fprintf(stderr, "Error: \"%s\" is not defined in this policy.\n", attr_name);
78 return -1;
79 }
80
81 if (attr->flavor != TYPE_ATTRIB) {
82 fprintf(stderr, "Error: \"%s\" is not an attribute in this policy.\n", attr_name);
83 return -1;
84 }
85
86 return attr->s.value - 1;
87 }
88
ebitmap_attribute_assertion_init(ebitmap_t * assertions,const char * const attributes[])89 static bool ebitmap_attribute_assertion_init(ebitmap_t *assertions, const char * const attributes[])
90 {
91
92 while (*attributes) {
93
94 int bit_pos = get_attr_bit(global_state.sepolicy.pdb, *attributes);
95 if (bit_pos < 0) {
96 /* get_attr_bit() logs error */
97 return false;
98 }
99
100 int err = ebitmap_set_bit(assertions, bit_pos, 1);
101 if (err) {
102 fprintf(stderr, "Error: setting bit on assertion ebitmap!\n");
103 return false;
104 }
105 attributes++;
106 }
107 return true;
108 }
109
is_type_of_attribute_set(policydb_t * policydb,const char * type_name,ebitmap_t * attr_set)110 static bool is_type_of_attribute_set(policydb_t *policydb, const char *type_name,
111 ebitmap_t *attr_set)
112 {
113 struct type_datum *type = hashtab_search(policydb->p_types.table, (char *)type_name);
114 if (!type) {
115 fprintf(stderr, "Error: \"%s\" is not defined in this policy.\n", type_name);
116 return false;
117 }
118
119 if (type->flavor != TYPE_TYPE) {
120 fprintf(stderr, "Error: \"%s\" is not a type in this policy.\n", type_name);
121 return false;
122 }
123
124 ebitmap_t dst;
125 ebitmap_init(&dst);
126
127 /* Take the intersection, if the set is empty, then its a failure */
128 int rc = ebitmap_and(&dst, attr_set, &policydb->type_attr_map[type->s.value - 1]);
129 if (rc) {
130 fprintf(stderr, "Error: Could not perform ebitmap_and: %d\n", rc);
131 exit(1);
132 }
133
134 bool res = (bool)ebitmap_length(&dst);
135
136 ebitmap_destroy(&dst);
137 return res;
138 }
139
dump_char_array(FILE * stream,const char * const * strings)140 static void dump_char_array(FILE *stream, const char * const *strings)
141 {
142
143 const char * const *p = strings;
144
145 fprintf(stream, "\"");
146
147 while (*p) {
148 const char *s = *p++;
149 const char *fmt = *p ? "%s, " : "%s\"";
150 fprintf(stream, fmt, s);
151 }
152 }
153
validate(char ** contextp)154 static int validate(char **contextp)
155 {
156 bool res;
157 char *context = *contextp;
158
159 sepol_context_t *ctx;
160 int rc = sepol_context_from_string(global_state.sepolicy.handle, context,
161 &ctx);
162 if (rc < 0) {
163 fprintf(stderr, "Error: Could not allocate context from string");
164 exit(1);
165 }
166
167 rc = sepol_context_check(global_state.sepolicy.handle,
168 global_state.sepolicy.sdb, ctx);
169 if (rc < 0) {
170 goto out;
171 }
172
173 const char *type_name = sepol_context_get_type(ctx);
174
175 // Temporarily exempt hal_power_stats_vendor_service from the check.
176 // TODO(b/211953546): remove this
177 if (strcmp(type_name, "hal_power_stats_vendor_service") == 0) {
178 goto out;
179 }
180
181 uint32_t len = ebitmap_length(&global_state.assert.set);
182 if (len > 0) {
183 res = !is_type_of_attribute_set(global_state.sepolicy.pdb, type_name,
184 &global_state.assert.set);
185 if (res) {
186 fprintf(stderr, "Error: type \"%s\" is not of set: ", type_name);
187 dump_char_array(stderr, global_state.assert.attrs);
188 fprintf(stderr, "\n");
189 /* The calls above did not affect rc, so set error before going to out */
190 rc = -1;
191 goto out;
192 }
193 }
194 /* Success: Although it should be 0, we explicitly set rc to 0 for clarity */
195 rc = 0;
196
197 out:
198 sepol_context_free(ctx);
199 return rc;
200 }
201
usage(char * name)202 static void usage(char *name) {
203 fprintf(stderr, "usage1: %s [-l|-p|-s|-v] [-e] sepolicy context_file\n\n"
204 "Parses a context file and checks for syntax errors.\n"
205 "If -p is specified, the property backend is used.\n"
206 "If -s is specified, the service backend is used to verify binder services.\n"
207 "If -l is specified, the service backend is used to verify hwbinder services.\n"
208 "If -v is specified, the service backend is used to verify vndbinder services.\n"
209 "Otherwise, context_file is assumed to be a file_contexts file\n"
210 "If -e is specified, then the context_file is allowed to be empty.\n\n"
211
212 "usage2: %s -c file_contexts1 file_contexts2\n\n"
213 "Compares two file contexts files and reports one of \n"
214 "subset, equal, superset, or incomparable.\n\n"
215
216 "usage3: %s -t file_contexts test_data\n\n"
217 "Validates a file contexts file against test_data.\n"
218 "test_data is a text file where each line has the format:\n"
219 " path expected_type\n\n\n",
220 name, name, name);
221 exit(1);
222 }
223
cleanup(void)224 static void cleanup(void) {
225
226 if (global_state.sepolicy.file) {
227 fclose(global_state.sepolicy.file);
228 }
229
230 if (global_state.sepolicy.sdb) {
231 sepol_policydb_free(global_state.sepolicy.sdb);
232 }
233
234 if (global_state.sepolicy.pf) {
235 sepol_policy_file_free(global_state.sepolicy.pf);
236 }
237
238 if (global_state.sepolicy.handle) {
239 sepol_handle_destroy(global_state.sepolicy.handle);
240 }
241
242 ebitmap_destroy(&global_state.assert.set);
243
244 int i;
245 for (i = 0; i < SEHANDLE_CNT; i++) {
246 struct selabel_handle *sehnd = global_state.sepolicy.sehnd[i];
247 if (sehnd) {
248 selabel_close(sehnd);
249 }
250 }
251 }
252
do_compare_and_die_on_error(struct selinux_opt opts[],unsigned int backend,char * paths[])253 static void do_compare_and_die_on_error(struct selinux_opt opts[], unsigned int backend, char *paths[])
254 {
255 enum selabel_cmp_result result;
256 char *result_str[] = { "subset", "equal", "superset", "incomparable" };
257 int i;
258
259 opts[0].value = NULL; /* not validating against a policy when comparing */
260
261 for (i = 0; i < SEHANDLE_CNT; i++) {
262 opts[1].value = paths[i];
263 global_state.sepolicy.sehnd[i] = selabel_open(backend, opts, 2);
264 if (!global_state.sepolicy.sehnd[i]) {
265 fprintf(stderr, "Error: could not load context file from %s\n", paths[i]);
266 exit(1);
267 }
268 }
269
270 result = selabel_cmp(global_state.sepolicy.sehnd[0], global_state.sepolicy.sehnd[1]);
271 printf("%s\n", result_str[result]);
272 }
273
274 static int warnings = 0;
log_callback(int type,const char * fmt,...)275 static int log_callback(int type, const char *fmt, ...) {
276 va_list ap;
277
278 if (type == SELINUX_WARNING) {
279 warnings += 1;
280 }
281 va_start(ap, fmt);
282 vfprintf(stderr, fmt, ap);
283 va_end(ap);
284 return 0;
285 }
286
do_test_data_and_die_on_error(struct selinux_opt opts[],unsigned int backend,char * paths[])287 static void do_test_data_and_die_on_error(struct selinux_opt opts[], unsigned int backend,
288 char *paths[])
289 {
290 opts[0].value = NULL; /* not validating against a policy */
291 opts[1].value = paths[0];
292 global_state.sepolicy.sehnd[0] = selabel_open(backend, opts, 2);
293 if (!global_state.sepolicy.sehnd[0]) {
294 fprintf(stderr, "Error: could not load context file from %s: %s\n",
295 paths[0], strerror(errno));
296 exit(1);
297 }
298
299 FILE* test_data = fopen(paths[1], "r");
300 if (test_data == NULL) {
301 fprintf(stderr, "Error: could not load test file from %s : %s\n",
302 paths[1], strerror(errno));
303 exit(1);
304 }
305
306 char line[1024];
307 while (fgets(line, sizeof(line), test_data)) {
308 char *path;
309 char *expected_type;
310
311 if (!strcmp(line, "\n") || line[0] == '#') {
312 continue;
313 }
314
315 int ret = sscanf(line, "%ms %ms", &path, &expected_type);
316 if (ret != 2) {
317 fprintf(stderr, "Error: unable to parse the line %s\n", line);
318 exit(1);
319 }
320
321 char *found_context;
322 ret = selabel_lookup(global_state.sepolicy.sehnd[0], &found_context, path, 0);
323 if (ret != 0) {
324 fprintf(stderr, "Error: unable to lookup the path for %s\n", line);
325 exit(1);
326 }
327
328 context_t found = context_new(found_context);
329 const char *found_type = context_type_get(found);
330
331 if (strcmp(found_type, expected_type)) {
332 fprintf(stderr, "Incorrect type for %s: resolved to %s, expected %s\n",
333 path, found_type, expected_type);
334 }
335
336 free(found_context);
337 context_free(found);
338 free(path);
339 free(expected_type);
340 }
341 fclose(test_data);
342
343 // Prints the coverage of file_contexts on the test data. It includes
344 // warnings for rules that have not been hit by any test example.
345 union selinux_callback cb;
346 cb.func_log = log_callback;
347 selinux_set_callback(SELINUX_CB_LOG, cb);
348 selabel_stats(global_state.sepolicy.sehnd[0]);
349 if (warnings) {
350 fprintf(stderr, "No test entries were found for the contexts above. " \
351 "You may need to update %s.\n", paths[1]);
352 exit(1);
353 }
354 }
355
do_fc_check_and_die_on_error(struct selinux_opt opts[],unsigned int backend,filemode mode,const char * sepolicy_file,const char * context_file,bool allow_empty)356 static void do_fc_check_and_die_on_error(struct selinux_opt opts[], unsigned int backend, filemode mode,
357 const char *sepolicy_file, const char *context_file, bool allow_empty)
358 {
359 struct stat sb;
360 if (stat(context_file, &sb) < 0) {
361 perror("Error: could not get stat on file contexts file");
362 exit(1);
363 }
364
365 if (sb.st_size == 0) {
366 /* Nothing to check on empty file_contexts file if allowed*/
367 if (allow_empty) {
368 return;
369 }
370 /* else: We could throw the error here, but libselinux backend will catch it */
371 }
372
373 global_state.sepolicy.file = fopen(sepolicy_file, "r");
374 if (!global_state.sepolicy.file) {
375 perror("Error: could not open policy file");
376 exit(1);
377 }
378
379 global_state.sepolicy.handle = sepol_handle_create();
380 if (!global_state.sepolicy.handle) {
381 fprintf(stderr, "Error: could not create policy handle: %s\n", strerror(errno));
382 exit(1);
383 }
384
385 if (sepol_policy_file_create(&global_state.sepolicy.pf) < 0) {
386 perror("Error: could not create policy handle");
387 exit(1);
388 }
389
390 sepol_policy_file_set_fp(global_state.sepolicy.pf, global_state.sepolicy.file);
391 sepol_policy_file_set_handle(global_state.sepolicy.pf, global_state.sepolicy.handle);
392
393 int rc = sepol_policydb_create(&global_state.sepolicy.sdb);
394 if (rc < 0) {
395 perror("Error: could not create policy db");
396 exit(1);
397 }
398
399 rc = sepol_policydb_read(global_state.sepolicy.sdb, global_state.sepolicy.pf);
400 if (rc < 0) {
401 perror("Error: could not read file into policy db");
402 exit(1);
403 }
404
405 global_state.assert.attrs = filemode_to_assert_attrs(mode);
406
407 bool ret = ebitmap_attribute_assertion_init(&global_state.assert.set, global_state.assert.attrs);
408 if (!ret) {
409 /* error messages logged by ebitmap_attribute_assertion_init() */
410 exit(1);
411 }
412
413 selinux_set_callback(SELINUX_CB_VALIDATE,
414 (union selinux_callback)&validate);
415
416 opts[1].value = context_file;
417
418 global_state.sepolicy.sehnd[0] = selabel_open(backend, opts, 2);
419 if (!global_state.sepolicy.sehnd[0]) {
420 fprintf(stderr, "Error: could not load context file from %s\n", context_file);
421 exit(1);
422 }
423 }
424
main(int argc,char ** argv)425 int main(int argc, char **argv)
426 {
427 struct selinux_opt opts[] = {
428 { SELABEL_OPT_VALIDATE, (void*)1 },
429 { SELABEL_OPT_PATH, NULL }
430 };
431
432 // Default backend unless changed by input argument.
433 unsigned int backend = SELABEL_CTX_FILE;
434
435 bool allow_empty = false;
436 bool compare = false;
437 bool test_data = false;
438 char c;
439
440 filemode mode = filemode_file_contexts;
441
442 while ((c = getopt(argc, argv, "clpsvet")) != -1) {
443 switch (c) {
444 case 'c':
445 compare = true;
446 break;
447 case 'e':
448 allow_empty = true;
449 break;
450 case 'p':
451 mode = filemode_property_contexts;
452 backend = SELABEL_CTX_ANDROID_PROP;
453 break;
454 case 's':
455 mode = filemode_service_contexts;
456 backend = SELABEL_CTX_ANDROID_SERVICE;
457 break;
458 case 'l':
459 mode = filemode_hw_service_contexts;
460 backend = SELABEL_CTX_ANDROID_SERVICE;
461 break;
462 case 'v':
463 mode = filemode_vendor_service_contexts;
464 backend = SELABEL_CTX_ANDROID_SERVICE;
465 break;
466 case 't':
467 test_data = true;
468 break;
469 case 'h':
470 default:
471 usage(argv[0]);
472 break;
473 }
474 }
475
476 int index = optind;
477 if (argc - optind != 2) {
478 usage(argv[0]);
479 }
480
481 if ((compare || test_data) && backend != SELABEL_CTX_FILE) {
482 usage(argv[0]);
483 }
484
485 atexit(cleanup);
486
487 if (compare) {
488 do_compare_and_die_on_error(opts, backend, &(argv[index]));
489 } else if (test_data) {
490 do_test_data_and_die_on_error(opts, backend, &(argv[index]));
491 } else {
492 /* remaining args are sepolicy file and context file */
493 char *sepolicy_file = argv[index];
494 char *context_file = argv[index + 1];
495
496 do_fc_check_and_die_on_error(opts, backend, mode, sepolicy_file, context_file, allow_empty);
497 }
498 exit(0);
499 }
500