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