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