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