1 #include <ctype.h>
2 #include <fcntl.h>
3 #include <getopt.h>
4 #include <stdbool.h>
5 #include <stdio.h>
6 #include <sys/mman.h>
7 #include <sys/stat.h>
8 #include <sys/types.h>
9 #include <unistd.h>
10
11 #include "neverallow.h"
12
13 static int debug;
14 static int warn;
15
neverallow_usage()16 void neverallow_usage() {
17 fprintf(stderr, "\tneverallow [-w|--warn] [-d|--debug] [-n|--neverallows <neverallow-rules>] | [-f|--file <neverallow-file>]\n");
18 }
19
read_typeset(policydb_t * policydb,char ** ptr,char * end,type_set_t * typeset,uint32_t * flags)20 static int read_typeset(policydb_t *policydb, char **ptr, char *end,
21 type_set_t *typeset, uint32_t *flags)
22 {
23 const char *keyword = "self";
24 size_t keyword_size = strlen(keyword), len;
25 char *p = *ptr;
26 unsigned openparens = 0;
27 char *start, *id;
28 type_datum_t *type;
29 struct ebitmap_node *n;
30 unsigned int bit;
31 bool negate = false;
32 int rc;
33
34 do {
35 while (p < end && isspace(*p))
36 p++;
37
38 if (p == end)
39 goto err;
40
41 if (*p == '~') {
42 if (debug)
43 printf(" ~");
44 typeset->flags = TYPE_COMP;
45 p++;
46 while (p < end && isspace(*p))
47 p++;
48 if (p == end)
49 goto err;
50 }
51
52 if (*p == '{') {
53 if (debug && !openparens)
54 printf(" {");
55 openparens++;
56 p++;
57 continue;
58 }
59
60 if (*p == '}') {
61 if (debug && openparens == 1)
62 printf(" }");
63 if (openparens == 0)
64 goto err;
65 openparens--;
66 p++;
67 continue;
68 }
69
70 if (*p == '*') {
71 if (debug)
72 printf(" *");
73 typeset->flags = TYPE_STAR;
74 p++;
75 continue;
76 }
77
78 if (*p == '-') {
79 if (debug)
80 printf(" -");
81 negate = true;
82 p++;
83 continue;
84 }
85
86 if (*p == '#') {
87 while (p < end && *p != '\n')
88 p++;
89 continue;
90 }
91
92 start = p;
93 while (p < end && !isspace(*p) && *p != ':' && *p != ';' && *p != '{' && *p != '}' && *p != '#')
94 p++;
95
96 if (p == start)
97 goto err;
98
99 len = p - start;
100 if (len == keyword_size && !strncmp(start, keyword, keyword_size)) {
101 if (debug)
102 printf(" self");
103 *flags |= RULE_SELF;
104 continue;
105 }
106
107 id = calloc(1, len + 1);
108 if (!id)
109 goto err;
110 memcpy(id, start, len);
111 if (debug)
112 printf(" %s", id);
113 type = hashtab_search(policydb->p_types.table, id);
114 if (!type) {
115 if (warn)
116 fprintf(stderr, "Warning! Type or attribute %s used in neverallow undefined in policy being checked.\n", id);
117 negate = false;
118 continue;
119 }
120 free(id);
121
122 if (type->flavor == TYPE_ATTRIB) {
123 if (negate)
124 rc = ebitmap_union(&typeset->negset, &policydb->attr_type_map[type->s.value - 1]);
125 else
126 rc = ebitmap_union(&typeset->types, &policydb->attr_type_map[type->s.value - 1]);
127 } else if (negate) {
128 rc = ebitmap_set_bit(&typeset->negset, type->s.value - 1, 1);
129 } else {
130 rc = ebitmap_set_bit(&typeset->types, type->s.value - 1, 1);
131 }
132
133 negate = false;
134
135 if (rc)
136 goto err;
137
138 } while (p < end && openparens);
139
140 if (p == end)
141 goto err;
142
143 if (typeset->flags & TYPE_STAR) {
144 for (bit = 0; bit < policydb->p_types.nprim; bit++) {
145 if (ebitmap_get_bit(&typeset->negset, bit))
146 continue;
147 if (policydb->type_val_to_struct[bit] &&
148 policydb->type_val_to_struct[bit]->flavor == TYPE_ATTRIB)
149 continue;
150 if (ebitmap_set_bit(&typeset->types, bit, 1))
151 goto err;
152 }
153 }
154
155 ebitmap_for_each_bit(&typeset->negset, n, bit) {
156 if (!ebitmap_node_get_bit(n, bit))
157 continue;
158 if (ebitmap_set_bit(&typeset->types, bit, 0))
159 goto err;
160 }
161
162 if (typeset->flags & TYPE_COMP) {
163 for (bit = 0; bit < policydb->p_types.nprim; bit++) {
164 if (policydb->type_val_to_struct[bit] &&
165 policydb->type_val_to_struct[bit]->flavor == TYPE_ATTRIB)
166 continue;
167 if (ebitmap_get_bit(&typeset->types, bit))
168 ebitmap_set_bit(&typeset->types, bit, 0);
169 else {
170 if (ebitmap_set_bit(&typeset->types, bit, 1))
171 goto err;
172 }
173 }
174 }
175
176 *ptr = p;
177 return 0;
178 err:
179 return -1;
180 }
181
read_classperms(policydb_t * policydb,char ** ptr,char * end,class_perm_node_t ** perms)182 static int read_classperms(policydb_t *policydb, char **ptr, char *end,
183 class_perm_node_t **perms)
184 {
185 char *p = *ptr;
186 unsigned openparens = 0;
187 char *id, *start;
188 class_datum_t *cls = NULL;
189 perm_datum_t *perm = NULL;
190 class_perm_node_t *classperms = NULL, *node = NULL;
191 bool complement = false;
192
193 while (p < end && isspace(*p))
194 p++;
195
196 if (p == end || *p != ':')
197 goto err;
198 p++;
199
200 if (debug)
201 printf(" :");
202
203 do {
204 while (p < end && isspace(*p))
205 p++;
206
207 if (p == end)
208 goto err;
209
210 if (*p == '{') {
211 if (debug && !openparens)
212 printf(" {");
213 openparens++;
214 p++;
215 continue;
216 }
217
218 if (*p == '}') {
219 if (debug && openparens == 1)
220 printf(" }");
221 if (openparens == 0)
222 goto err;
223 openparens--;
224 p++;
225 continue;
226 }
227
228 if (*p == '#') {
229 while (p < end && *p != '\n')
230 p++;
231 continue;
232 }
233
234 start = p;
235 while (p < end && !isspace(*p) && *p != '{' && *p != '}' && *p != ';' && *p != '#')
236 p++;
237
238 if (p == start)
239 goto err;
240
241 id = calloc(1, p - start + 1);
242 if (!id)
243 goto err;
244 memcpy(id, start, p - start);
245 if (debug)
246 printf(" %s", id);
247 cls = hashtab_search(policydb->p_classes.table, id);
248 if (!cls) {
249 if (warn)
250 fprintf(stderr, "Warning! Class %s used in neverallow undefined in policy being checked.\n", id);
251 continue;
252 }
253
254 node = calloc(1, sizeof *node);
255 if (!node)
256 goto err;
257 node->tclass = cls->s.value;
258 node->next = classperms;
259 classperms = node;
260 free(id);
261 id = NULL;
262 } while (p < end && openparens);
263
264 if (p == end)
265 goto err;
266
267 if (warn && !classperms)
268 fprintf(stderr, "Warning! Empty class set\n");
269
270 do {
271 while (p < end && isspace(*p))
272 p++;
273
274 if (p == end)
275 goto err;
276
277 if (*p == '~') {
278 if (debug)
279 printf(" ~");
280 complement = true;
281 p++;
282 while (p < end && isspace(*p))
283 p++;
284 if (p == end)
285 goto err;
286 }
287
288 if (*p == '{') {
289 if (debug && !openparens)
290 printf(" {");
291 openparens++;
292 p++;
293 continue;
294 }
295
296 if (*p == '}') {
297 if (debug && openparens == 1)
298 printf(" }");
299 if (openparens == 0)
300 goto err;
301 openparens--;
302 p++;
303 continue;
304 }
305
306 if (*p == '#') {
307 while (p < end && *p != '\n')
308 p++;
309 continue;
310 }
311
312 start = p;
313 while (p < end && !isspace(*p) && *p != '{' && *p != '}' && *p != ';' && *p != '#')
314 p++;
315
316 if (p == start)
317 goto err;
318
319 id = calloc(1, p - start + 1);
320 if (!id)
321 goto err;
322 memcpy(id, start, p - start);
323 if (debug)
324 printf(" %s", id);
325
326 if (!strcmp(id, "*")) {
327 for (node = classperms; node; node = node->next)
328 node->data = ~0;
329 free(id);
330 id = NULL;
331 continue;
332 }
333
334 for (node = classperms; node; node = node->next) {
335 cls = policydb->class_val_to_struct[node->tclass-1];
336 perm = hashtab_search(cls->permissions.table, id);
337 if (cls->comdatum && !perm)
338 perm = hashtab_search(cls->comdatum->permissions.table, id);
339 if (!perm) {
340 if (warn)
341 fprintf(stderr, "Warning! Permission %s used in neverallow undefined in class %s in policy being checked.\n", id, policydb->p_class_val_to_name[node->tclass-1]);
342 continue;
343 }
344 node->data |= 1U << (perm->s.value - 1);
345 }
346 free(id);
347 id = NULL;
348 } while (p < end && openparens);
349
350 if (p == end)
351 goto err;
352
353 if (complement) {
354 for (node = classperms; node; node = node->next)
355 node->data = ~node->data;
356 }
357
358 if (warn) {
359 for (node = classperms; node; node = node->next)
360 if (!node->data)
361 fprintf(stderr, "Warning! Empty permission set\n");
362 }
363
364 *perms = classperms;
365 *ptr = p;
366 return 0;
367 err:
368 // free classperms memory
369 for (node = classperms; node; ) {
370 class_perm_node_t *freeptr = node;
371 node = node->next;
372 free(freeptr);
373 }
374 return -1;
375 }
376
check_neverallows(policydb_t * policydb,char * text,char * end)377 static int check_neverallows(policydb_t *policydb, char *text, char *end)
378 {
379 const char *keyword = "neverallow";
380 size_t keyword_size = strlen(keyword), len;
381 struct avrule *neverallows = NULL, *avrule = NULL;
382 char *p, *start;
383 int result;
384
385 int non_comment_len = 0, cur_non_comment_len = 0;
386 char *cur_non_comment_text = calloc(1, (end - text) + 1);
387 char *non_comment_text = cur_non_comment_text;
388 if (!cur_non_comment_text)
389 goto err;
390 p = text;
391 bool in_comment = false;
392 while (p < end) {
393 if (*p == '#') in_comment = true;
394 if (!in_comment || *p == '\n') *cur_non_comment_text++ = *p;
395 if (*p == '\n') in_comment = false;
396 ++p;
397 }
398 p = non_comment_text;
399 end = cur_non_comment_text;
400 while (p < end) {
401 while (p < end && isspace(*p)) p++;
402 start = p;
403 while (p < end && !isspace(*p)) p++;
404 len = p - start;
405 if (len != keyword_size || strncmp(start, keyword, keyword_size))
406 continue;
407
408 if (debug)
409 printf("neverallow");
410
411 avrule = calloc(1, sizeof *avrule);
412 if (!avrule)
413 goto err;
414
415 avrule->specified = AVRULE_NEVERALLOW;
416
417 if (read_typeset(policydb, &p, end, &avrule->stypes, &avrule->flags))
418 goto err;
419
420 if (read_typeset(policydb, &p, end, &avrule->ttypes, &avrule->flags))
421 goto err;
422
423 if (read_classperms(policydb, &p, end, &avrule->perms))
424 goto err;
425
426 while (p < end && *p != ';')
427 p++;
428
429 if (p == end || *p != ';')
430 goto err;
431
432 if (debug)
433 printf(";\n");
434
435 avrule->next = neverallows;
436 neverallows = avrule;
437 }
438
439 if (!neverallows)
440 goto err;
441
442 result = check_assertions(NULL, policydb, neverallows);
443 avrule_list_destroy(neverallows);
444 free(non_comment_text);
445 return result;
446 err:
447 free(non_comment_text);
448 if (errno == ENOMEM) {
449 fprintf(stderr, "Out of memory while parsing neverallow rules\n");
450 } else
451 fprintf(stderr, "Error while parsing neverallow rules\n");
452
453 avrule_list_destroy(neverallows);
454 if (avrule != neverallows)
455 avrule_destroy(avrule);
456
457 return -1;
458 }
459
check_neverallows_file(policydb_t * policydb,const char * filename)460 static int check_neverallows_file(policydb_t *policydb, const char *filename)
461 {
462 int fd;
463 struct stat sb;
464 char *text, *end;
465
466 fd = open(filename, O_RDONLY);
467 if (fd < 0) {
468 fprintf(stderr, "Could not open %s: %s\n", filename, strerror(errno));
469 return -1;
470 }
471 if (fstat(fd, &sb) < 0) {
472 fprintf(stderr, "Can't stat '%s': %s\n", filename, strerror(errno));
473 close(fd);
474 return -1;
475 }
476 text = mmap(NULL, sb.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
477 end = text + sb.st_size;
478 if (text == MAP_FAILED) {
479 fprintf(stderr, "Can't mmap '%s': %s\n", filename, strerror(errno));
480 close(fd);
481 return -1;
482 }
483 close(fd);
484 return check_neverallows(policydb, text, end);
485 }
486
check_neverallows_string(policydb_t * policydb,char * string,size_t len)487 static int check_neverallows_string(policydb_t *policydb, char *string, size_t len)
488 {
489 char *text, *end;
490 text = string;
491 end = text + len;
492 return check_neverallows(policydb, text, end);
493 }
494
neverallow_func(int argc,char ** argv,policydb_t * policydb)495 int neverallow_func (int argc, char **argv, policydb_t *policydb) {
496 char *rules = 0, *file = 0;
497 char ch;
498
499 struct option neverallow_options[] = {
500 {"debug", no_argument, NULL, 'd'},
501 {"file_input", required_argument, NULL, 'f'},
502 {"neverallow", required_argument, NULL, 'n'},
503 {"warn", no_argument, NULL, 'w'},
504 {NULL, 0, NULL, 0}
505 };
506
507 while ((ch = getopt_long(argc, argv, "df:n:w", neverallow_options, NULL)) != -1) {
508 switch (ch) {
509 case 'd':
510 debug = 1;
511 break;
512 case 'f':
513 file = optarg;
514 break;
515 case 'n':
516 rules = optarg;
517 break;
518 case 'w':
519 warn = 1;
520 break;
521 default:
522 USAGE_ERROR = true;
523 return -1;
524 }
525 }
526
527 if (!(rules || file) || (rules && file)){
528 USAGE_ERROR = true;
529 return -1;
530 }
531 if (file) {
532 return check_neverallows_file(policydb, file);
533 } else {
534 return check_neverallows_string(policydb, rules, strlen(rules));
535 }
536 }
537