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     p = text;
386     while (p < end) {
387         while (p < end && isspace(*p))
388             p++;
389 
390         if (*p == '#') {
391             while (p < end && *p != '\n')
392                 p++;
393             continue;
394         }
395 
396         start = p;
397         while (p < end && !isspace(*p))
398             p++;
399 
400         len = p - start;
401         if (len != keyword_size || strncmp(start, keyword, keyword_size))
402             continue;
403 
404         if (debug)
405             printf("neverallow");
406 
407         avrule = calloc(1, sizeof *avrule);
408         if (!avrule)
409             goto err;
410 
411         avrule->specified = AVRULE_NEVERALLOW;
412 
413         if (read_typeset(policydb, &p, end, &avrule->stypes, &avrule->flags))
414             goto err;
415 
416         if (read_typeset(policydb, &p, end, &avrule->ttypes, &avrule->flags))
417             goto err;
418 
419         if (read_classperms(policydb, &p, end, &avrule->perms))
420             goto err;
421 
422         while (p < end && *p != ';')
423             p++;
424 
425         if (p == end || *p != ';')
426             goto err;
427 
428         if (debug)
429             printf(";\n");
430 
431         avrule->next = neverallows;
432         neverallows = avrule;
433     }
434 
435     if (!neverallows)
436         goto err;
437 
438     result = check_assertions(NULL, policydb, neverallows);
439     avrule_list_destroy(neverallows);
440     return result;
441 err:
442     if (errno == ENOMEM) {
443         fprintf(stderr, "Out of memory while parsing neverallow rules\n");
444     } else
445         fprintf(stderr, "Error while parsing neverallow rules\n");
446 
447     avrule_list_destroy(neverallows);
448     if (avrule != neverallows)
449         avrule_destroy(avrule);
450 
451     return -1;
452 }
453 
check_neverallows_file(policydb_t * policydb,const char * filename)454 static int check_neverallows_file(policydb_t *policydb, const char *filename)
455 {
456     int fd;
457     struct stat sb;
458     char *text, *end;
459 
460     fd = open(filename, O_RDONLY);
461     if (fd < 0) {
462         fprintf(stderr, "Could not open %s:  %s\n", filename, strerror(errno));
463         return -1;
464     }
465     if (fstat(fd, &sb) < 0) {
466         fprintf(stderr, "Can't stat '%s':  %s\n", filename, strerror(errno));
467         close(fd);
468         return -1;
469     }
470     text = mmap(NULL, sb.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
471     end = text + sb.st_size;
472     if (text == MAP_FAILED) {
473         fprintf(stderr, "Can't mmap '%s':  %s\n", filename, strerror(errno));
474         close(fd);
475         return -1;
476     }
477     close(fd);
478     return check_neverallows(policydb, text, end);
479 }
480 
check_neverallows_string(policydb_t * policydb,char * string,size_t len)481 static int check_neverallows_string(policydb_t *policydb, char *string, size_t len)
482 {
483     char *text, *end;
484     text = string;
485     end = text + len;
486     return check_neverallows(policydb, text, end);
487 }
488 
neverallow_func(int argc,char ** argv,policydb_t * policydb)489 int neverallow_func (int argc, char **argv, policydb_t *policydb) {
490     char *rules = 0, *file = 0;
491     char ch;
492 
493     struct option neverallow_options[] = {
494         {"debug", no_argument, NULL, 'd'},
495         {"file_input", required_argument, NULL, 'f'},
496         {"neverallow", required_argument, NULL, 'n'},
497         {"warn", no_argument, NULL, 'w'},
498         {NULL, 0, NULL, 0}
499     };
500 
501     while ((ch = getopt_long(argc, argv, "df:n:w", neverallow_options, NULL)) != -1) {
502         switch (ch) {
503         case 'd':
504             debug = 1;
505             break;
506         case 'f':
507             file = optarg;
508             break;
509         case 'n':
510             rules = optarg;
511             break;
512         case 'w':
513             warn = 1;
514             break;
515         default:
516             USAGE_ERROR = true;
517             return -1;
518         }
519     }
520 
521     if (!(rules || file) || (rules && file)){
522         USAGE_ERROR = true;
523         return -1;
524     }
525     if (file) {
526         return check_neverallows_file(policydb, file);
527     } else {
528         return check_neverallows_string(policydb, rules, strlen(rules));
529     }
530 }
531