• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
filemode_to_assert_attrs(filemode mode)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 
get_attr_bit(policydb_t * policydb,const char * attr_name)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 
ebitmap_attribute_assertion_init(ebitmap_t * assertions,const char * const attributes[])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 
is_type_of_attribute_set(policydb_t * policydb,const char * type_name,ebitmap_t * attr_set)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 
dump_char_array(FILE * stream,const char * const * strings)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 
validate(char ** contextp)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 
usage(char * name)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 
cleanup(void)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 
do_compare_and_die_on_error(struct selinux_opt opts[],unsigned int backend,char * paths[])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 
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)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 
main(int argc,char ** argv)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