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