checkfc.c 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398
  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/selinux.h>
  11. #include <selinux/label.h>
  12. #include <sys/stat.h>
  13. #include <sys/types.h>
  14. static const char * const CHECK_FC_ASSERT_ATTRS[] = { "fs_type", "dev_type", "file_type", NULL };
  15. static const char * const CHECK_PC_ASSERT_ATTRS[] = { "property_type", NULL };
  16. static const char * const CHECK_SC_ASSERT_ATTRS[] = { "service_manager_type", NULL };
  17. static const char * const CHECK_HW_SC_ASSERT_ATTRS[] = { "hwservice_manager_type", NULL };
  18. static const char * const CHECK_VND_SC_ASSERT_ATTRS[] = { "vndservice_manager_type", NULL };
  19. typedef enum filemode filemode;
  20. enum filemode {
  21. filemode_file_contexts = 0,
  22. filemode_property_contexts,
  23. filemode_service_contexts,
  24. filemode_hw_service_contexts,
  25. filemode_vendor_service_contexts
  26. };
  27. static struct {
  28. /* policy */
  29. struct {
  30. union {
  31. /* Union these so we don't have to cast */
  32. sepol_policydb_t *sdb;
  33. policydb_t *pdb;
  34. };
  35. sepol_policy_file_t *pf;
  36. sepol_handle_t *handle;
  37. FILE *file;
  38. #define SEHANDLE_CNT 2
  39. struct selabel_handle *sehnd[SEHANDLE_CNT];
  40. } sepolicy;
  41. /* assertions */
  42. struct {
  43. const char * const *attrs; /* for the original set to print on error */
  44. ebitmap_t set; /* the ebitmap representation of the attrs */
  45. } assert;
  46. } global_state;
  47. static const char * const *filemode_to_assert_attrs(filemode mode)
  48. {
  49. switch (mode) {
  50. case filemode_file_contexts:
  51. return CHECK_FC_ASSERT_ATTRS;
  52. case filemode_property_contexts:
  53. return CHECK_PC_ASSERT_ATTRS;
  54. case filemode_service_contexts:
  55. return CHECK_SC_ASSERT_ATTRS;
  56. case filemode_hw_service_contexts:
  57. return CHECK_HW_SC_ASSERT_ATTRS;
  58. case filemode_vendor_service_contexts:
  59. return CHECK_VND_SC_ASSERT_ATTRS;
  60. }
  61. /* die on invalid parameters */
  62. fprintf(stderr, "Error: Invalid mode of operation: %d\n", mode);
  63. exit(1);
  64. }
  65. static int get_attr_bit(policydb_t *policydb, const char *attr_name)
  66. {
  67. struct type_datum *attr = hashtab_search(policydb->p_types.table, (char *)attr_name);
  68. if (!attr) {
  69. fprintf(stderr, "Error: \"%s\" is not defined in this policy.\n", attr_name);
  70. return -1;
  71. }
  72. if (attr->flavor != TYPE_ATTRIB) {
  73. fprintf(stderr, "Error: \"%s\" is not an attribute in this policy.\n", attr_name);
  74. return -1;
  75. }
  76. return attr->s.value - 1;
  77. }
  78. static bool ebitmap_attribute_assertion_init(ebitmap_t *assertions, const char * const attributes[])
  79. {
  80. while (*attributes) {
  81. int bit_pos = get_attr_bit(global_state.sepolicy.pdb, *attributes);
  82. if (bit_pos < 0) {
  83. /* get_attr_bit() logs error */
  84. return false;
  85. }
  86. int err = ebitmap_set_bit(assertions, bit_pos, 1);
  87. if (err) {
  88. fprintf(stderr, "Error: setting bit on assertion ebitmap!\n");
  89. return false;
  90. }
  91. attributes++;
  92. }
  93. return true;
  94. }
  95. static bool is_type_of_attribute_set(policydb_t *policydb, const char *type_name,
  96. ebitmap_t *attr_set)
  97. {
  98. struct type_datum *type = hashtab_search(policydb->p_types.table, (char *)type_name);
  99. if (!type) {
  100. fprintf(stderr, "Error: \"%s\" is not defined in this policy.\n", type_name);
  101. return false;
  102. }
  103. if (type->flavor != TYPE_TYPE) {
  104. fprintf(stderr, "Error: \"%s\" is not a type in this policy.\n", type_name);
  105. return false;
  106. }
  107. ebitmap_t dst;
  108. ebitmap_init(&dst);
  109. /* Take the intersection, if the set is empty, then its a failure */
  110. int rc = ebitmap_and(&dst, attr_set, &policydb->type_attr_map[type->s.value - 1]);
  111. if (rc) {
  112. fprintf(stderr, "Error: Could not perform ebitmap_and: %d\n", rc);
  113. exit(1);
  114. }
  115. bool res = (bool)ebitmap_length(&dst);
  116. ebitmap_destroy(&dst);
  117. return res;
  118. }
  119. static void dump_char_array(FILE *stream, const char * const *strings)
  120. {
  121. const char * const *p = strings;
  122. fprintf(stream, "\"");
  123. while (*p) {
  124. const char *s = *p++;
  125. const char *fmt = *p ? "%s, " : "%s\"";
  126. fprintf(stream, fmt, s);
  127. }
  128. }
  129. static int validate(char **contextp)
  130. {
  131. bool res;
  132. char *context = *contextp;
  133. sepol_context_t *ctx;
  134. int rc = sepol_context_from_string(global_state.sepolicy.handle, context,
  135. &ctx);
  136. if (rc < 0) {
  137. fprintf(stderr, "Error: Could not allocate context from string");
  138. exit(1);
  139. }
  140. rc = sepol_context_check(global_state.sepolicy.handle,
  141. global_state.sepolicy.sdb, ctx);
  142. if (rc < 0) {
  143. goto out;
  144. }
  145. const char *type_name = sepol_context_get_type(ctx);
  146. uint32_t len = ebitmap_length(&global_state.assert.set);
  147. if (len > 0) {
  148. res = !is_type_of_attribute_set(global_state.sepolicy.pdb, type_name,
  149. &global_state.assert.set);
  150. if (res) {
  151. fprintf(stderr, "Error: type \"%s\" is not of set: ", type_name);
  152. dump_char_array(stderr, global_state.assert.attrs);
  153. fprintf(stderr, "\n");
  154. /* The calls above did not affect rc, so set error before going to out */
  155. rc = -1;
  156. goto out;
  157. }
  158. }
  159. /* Success: Although it should be 0, we explicitly set rc to 0 for clarity */
  160. rc = 0;
  161. out:
  162. sepol_context_free(ctx);
  163. return rc;
  164. }
  165. static void usage(char *name) {
  166. fprintf(stderr, "usage1: %s [-l|-p|-s|-v] [-e] sepolicy context_file\n\n"
  167. "Parses a context file and checks for syntax errors.\n"
  168. "If -p is specified, the property backend is used.\n"
  169. "If -s is specified, the service backend is used to verify binder services.\n"
  170. "If -l is specified, the service backend is used to verify hwbinder services.\n"
  171. "If -v is specified, the service backend is used to verify vndbinder services.\n"
  172. "Otherwise, context_file is assumed to be a file_contexts file\n"
  173. "If -e is specified, then the context_file is allowed to be empty.\n\n"
  174. "usage2: %s -c file_contexts1 file_contexts2\n\n"
  175. "Compares two file contexts files and reports one of subset, equal, superset, or incomparable.\n\n",
  176. name, name);
  177. exit(1);
  178. }
  179. static void cleanup(void) {
  180. if (global_state.sepolicy.file) {
  181. fclose(global_state.sepolicy.file);
  182. }
  183. if (global_state.sepolicy.sdb) {
  184. sepol_policydb_free(global_state.sepolicy.sdb);
  185. }
  186. if (global_state.sepolicy.pf) {
  187. sepol_policy_file_free(global_state.sepolicy.pf);
  188. }
  189. if (global_state.sepolicy.handle) {
  190. sepol_handle_destroy(global_state.sepolicy.handle);
  191. }
  192. ebitmap_destroy(&global_state.assert.set);
  193. int i;
  194. for (i = 0; i < SEHANDLE_CNT; i++) {
  195. struct selabel_handle *sehnd = global_state.sepolicy.sehnd[i];
  196. if (sehnd) {
  197. selabel_close(sehnd);
  198. }
  199. }
  200. }
  201. static void do_compare_and_die_on_error(struct selinux_opt opts[], unsigned int backend, char *paths[])
  202. {
  203. enum selabel_cmp_result result;
  204. char *result_str[] = { "subset", "equal", "superset", "incomparable" };
  205. int i;
  206. opts[0].value = NULL; /* not validating against a policy when comparing */
  207. for (i = 0; i < SEHANDLE_CNT; i++) {
  208. opts[1].value = paths[i];
  209. global_state.sepolicy.sehnd[i] = selabel_open(backend, opts, 2);
  210. if (!global_state.sepolicy.sehnd[i]) {
  211. fprintf(stderr, "Error: could not load context file from %s\n", paths[i]);
  212. exit(1);
  213. }
  214. }
  215. result = selabel_cmp(global_state.sepolicy.sehnd[0], global_state.sepolicy.sehnd[1]);
  216. printf("%s\n", result_str[result]);
  217. }
  218. static void do_fc_check_and_die_on_error(struct selinux_opt opts[], unsigned int backend, filemode mode,
  219. const char *sepolicy_file, const char *context_file, bool allow_empty)
  220. {
  221. struct stat sb;
  222. if (stat(context_file, &sb) < 0) {
  223. perror("Error: could not get stat on file contexts file");
  224. exit(1);
  225. }
  226. if (sb.st_size == 0) {
  227. /* Nothing to check on empty file_contexts file if allowed*/
  228. if (allow_empty) {
  229. return;
  230. }
  231. /* else: We could throw the error here, but libselinux backend will catch it */
  232. }
  233. global_state.sepolicy.file = fopen(sepolicy_file, "r");
  234. if (!global_state.sepolicy.file) {
  235. perror("Error: could not open policy file");
  236. exit(1);
  237. }
  238. global_state.sepolicy.handle = sepol_handle_create();
  239. if (!global_state.sepolicy.handle) {
  240. fprintf(stderr, "Error: could not create policy handle: %s\n", strerror(errno));
  241. exit(1);
  242. }
  243. if (sepol_policy_file_create(&global_state.sepolicy.pf) < 0) {
  244. perror("Error: could not create policy handle");
  245. exit(1);
  246. }
  247. sepol_policy_file_set_fp(global_state.sepolicy.pf, global_state.sepolicy.file);
  248. sepol_policy_file_set_handle(global_state.sepolicy.pf, global_state.sepolicy.handle);
  249. int rc = sepol_policydb_create(&global_state.sepolicy.sdb);
  250. if (rc < 0) {
  251. perror("Error: could not create policy db");
  252. exit(1);
  253. }
  254. rc = sepol_policydb_read(global_state.sepolicy.sdb, global_state.sepolicy.pf);
  255. if (rc < 0) {
  256. perror("Error: could not read file into policy db");
  257. exit(1);
  258. }
  259. global_state.assert.attrs = filemode_to_assert_attrs(mode);
  260. bool ret = ebitmap_attribute_assertion_init(&global_state.assert.set, global_state.assert.attrs);
  261. if (!ret) {
  262. /* error messages logged by ebitmap_attribute_assertion_init() */
  263. exit(1);
  264. }
  265. selinux_set_callback(SELINUX_CB_VALIDATE,
  266. (union selinux_callback)&validate);
  267. opts[1].value = context_file;
  268. global_state.sepolicy.sehnd[0] = selabel_open(backend, opts, 2);
  269. if (!global_state.sepolicy.sehnd[0]) {
  270. fprintf(stderr, "Error: could not load context file from %s\n", context_file);
  271. exit(1);
  272. }
  273. }
  274. int main(int argc, char **argv)
  275. {
  276. struct selinux_opt opts[] = {
  277. { SELABEL_OPT_VALIDATE, (void*)1 },
  278. { SELABEL_OPT_PATH, NULL }
  279. };
  280. // Default backend unless changed by input argument.
  281. unsigned int backend = SELABEL_CTX_FILE;
  282. bool allow_empty = false;
  283. bool compare = false;
  284. char c;
  285. filemode mode = filemode_file_contexts;
  286. while ((c = getopt(argc, argv, "clpsve")) != -1) {
  287. switch (c) {
  288. case 'c':
  289. compare = true;
  290. break;
  291. case 'e':
  292. allow_empty = true;
  293. break;
  294. case 'p':
  295. mode = filemode_property_contexts;
  296. backend = SELABEL_CTX_ANDROID_PROP;
  297. break;
  298. case 's':
  299. mode = filemode_service_contexts;
  300. backend = SELABEL_CTX_ANDROID_SERVICE;
  301. break;
  302. case 'l':
  303. mode = filemode_hw_service_contexts;
  304. backend = SELABEL_CTX_ANDROID_SERVICE;
  305. break;
  306. case 'v':
  307. mode = filemode_vendor_service_contexts;
  308. backend = SELABEL_CTX_ANDROID_SERVICE;
  309. break;
  310. case 'h':
  311. default:
  312. usage(argv[0]);
  313. break;
  314. }
  315. }
  316. int index = optind;
  317. if (argc - optind != 2) {
  318. usage(argv[0]);
  319. }
  320. if (compare && backend != SELABEL_CTX_FILE) {
  321. usage(argv[0]);
  322. }
  323. atexit(cleanup);
  324. if (compare) {
  325. do_compare_and_die_on_error(opts, backend, &(argv[index]));
  326. } else {
  327. /* remaining args are sepolicy file and context file */
  328. char *sepolicy_file = argv[index];
  329. char *context_file = argv[index + 1];
  330. do_fc_check_and_die_on_error(opts, backend, mode, sepolicy_file, context_file, allow_empty);
  331. }
  332. exit(0);
  333. }